Django: почему annotate добавляет элементы в мой кверисет?
Я пытаюсь сделать что-то простое.
У меня есть объекты Item
, и пользователи могут отмечать их как избранные.
Так как эти объекты не принадлежат пользователю, я решил использовать ManyToMany между User
и Item
для записи отношения избранности. Если пользователь находится в поле favoriters
элемента, это означает, что пользователь отметил его как избранный.
Затем, когда я получаю объекты для определенного пользователя, я хочу аннотировать каждый элемент, чтобы указать, является ли он избранным пользователем. Для этого я создал метод add_is_favorite_for()
.
Вот (упрощенный) код:
class ItemQuerySet(query.QuerySet):
def add_is_favorite_for(self, user):
"""add a boolean to know if the item is favorited by the given user"""
condition = Q(favoriters=user)
return self.annotate(is_favorite=ExpressionWrapper(condition, output_field=BooleanField()))
class Item(models.Model):
objects = Manager.from_queryset(ItemQuerySet)()
favoriters = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True)
Это не работает так, как ожидалось, кажется, что Django добавляет элемент для каждого пользователя, который отметил элемент. Это приводит к таким безумным вещам, как:
Item.objects.count() # 10
Item.objects.add_is_favorite_for(some_user).count() # 16 -> how the hell an annotation can return more results than initial queryset?
Я что-то упускаю...
Вы можете изменить условие в аннотации, чтобы использовать подзапрос, получить все элементы, которые пользователь выбрал, а затем проверить, есть ли id элемента в подзапросе.
Это должно устранить вашу проблему с дублированием:
def add_is_favorite_for(self, user):
return self.annotate(is_favorite=ExpressionWrapper(
Q(id__in=Item.objects.filter(favoriters=user).values('id')),
output_field=BooleanField()
))