Различные значения в аннотированном поле ArrayAgg с использованием Django 1.11 и PostgreSQL
Я пытаюсь аннотировать поле queryset, содержащее список uuids учетных записей пользователей, заинтересованных и не заинтересованных в участии в каком-либо мероприятии. Этот проект использует Django 1.11 и PostgreSQL, поэтому я хотел аннотировать этот список с помощью ArrayAgg. К сожалению, эта версия Django не поддерживает distinct kwarg в ArrayAgg, поэтому список, который я получаю, содержит дублирующиеся элементы. То, что я пробовал:
- Использование свойств на модели - это работает хорошо и результаты хорошие, но вместо 2 запросов за 10 мс выполняется ~300 запросов за 200 мс. Это на БД разработки, поэтому на продакшене это будет гораздо заметнее.
- Я реализовал свои собственные
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