Различные значения в аннотированном поле ArrayAgg с использованием Django 1.11 и PostgreSQL

Я пытаюсь аннотировать поле queryset, содержащее список uuids учетных записей пользователей, заинтересованных и не заинтересованных в участии в каком-либо мероприятии. Этот проект использует Django 1.11 и PostgreSQL, поэтому я хотел аннотировать этот список с помощью ArrayAgg. К сожалению, эта версия Django не поддерживает distinct kwarg в ArrayAgg, поэтому список, который я получаю, содержит дублирующиеся элементы. То, что я пробовал:

  1. Использование свойств на модели - это работает хорошо и результаты хорошие, но вместо 2 запросов за 10 мс выполняется ~300 запросов за 200 мс. Это на БД разработки, поэтому на продакшене это будет гораздо заметнее.
  2. Я реализовал свои собственные ArrayAgg и Aggregate, используя код из Django 2.0 repo, и это работает хорошо, я получаю желаемые 2 запроса, но есть ли лучший способ, чтобы я мог избежать такого "халтурного" решения?

Я не могу обновить Django до версии 2.0

Пример кода: Версия с отображаемыми дубликатами

models.py

import uuid as uuid
from django.db import models


class Account(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4)


class MyUser(models.Model):
    account = models.OneToOneField(Account, on_delete=models.CASCADE)


class InterestStatus(models.Model):
    name = models.CharField(max_length=100)
    interested = models.ManyToManyField(MyUser, related_name='interested')
    not_interested = models.ManyToManyField(MyUser, related_name='not_interested')

    @property
    def interested_users_uids(self):
        ids = []
        for user in self.interested.all():
            ids.append(user.account.uuid)
        return ids

    @property
    def not_interested_users_uids(self):
        ids = []
        for user in self.not_interested.all():
            ids.append(user.account.uuid)
        return ids

views.py

from django.contrib.postgres.aggregates import ArrayAgg
from rest_framework import viewsets

from test_pap.models import InterestStatus
from test_pap.serializers import InterestStatusSerializer


class InterestStatusViewset(viewsets.ModelViewSet):
    queryset = InterestStatus.objects.all()
    serializer_class = InterestStatusSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        qs = qs.annotate(
            annotated_interested_uids=ArrayAgg('interested__account__uuid'),
            annotated_not_interested_uids=ArrayAgg('not_interested__account__uuid'),
        )
        return qs

serializers.py

class InterestStatusSerializer(serializers.ModelSerializer):
    interested_users_uids = serializers.ListField(child=serializers.UUIDField(), source='annotated_interested_uids')
    not_interested_users_uids = serializers.ListField(child=serializers.UUIDField(), source='annotated_not_interested_uids')

    class Meta:
        model = InterestStatus
        fields = [
            'name',
            'interested_users_uids',
            'not_interested_users_uids',
        ]

Вы можете использовать функцию StringAgg для получения отдельных uuids, а затем использовать сериализатор для разделения строки и преобразования ее в список uuids.

class InterestStatusViewset(viewsets.ModelViewSet):
   queryset = InterestStatus.objects.all()
   serializer_class = InterestStatusSerializer

   def get_queryset(self):
      qs = super().get_queryset()
      qs = qs.annotate(
        annotated_interested_uids=StringAgg('interested__account__uuid',',',distinct=True),
        annotated_not_interested_uids=StringAgg('not_interested__account__uuid',',',distinct=True),
    )
    return qs

Сериализатор может быть модифицирован следующим образом для получения требуемого вывода с помощью SerializerMethodField

class InterestStatusSerializer(serializers.ModelSerializer):
    interested_users_uids = serializers.SerializerMethodField()
    not_interested_users_uids = serializers.SerializerMethodField()

    class Meta:
        model = InterestStatus
        fields = [
            'name',
            'interested_users_uids',
            'not_interested_users_uids',
        ]

    def get_interested_users_uids(self, obj):
        uuids = []
        if obj.get('annotated_interested_uids'):
            uuids = obj.get('annotated_interested_uids').split(',')
        return uuids

    def get_not_interested_users_uids(self, obj):
        uuids = []
        if obj.get('annotated_not_interested_uids'):
            uuids = obj.get('annotated_not_interested_uids').split(',')
        return uuids
Вернуться на верх