Есть две простые модели:
class Question(TimeStampedModel):
text = models.CharField(max_length=40)
class Answer(TimeStampedModel):
question = models.ForeignKey(Question, related_name='answers')
is_agreed = models.BooleanField()
author = models.ForeingKey(User, related_name='answers')
И есть моя проблема:
In [18]: Question.objects.count()
Out[18]: 3
Мне нужно аннотировать набор запросов с помощью полей is_user_agreed и answers_amount:
In [18]: user = User.objects.first()
In [19]: qs = Question.objects.annotate(
...: is_user_agreed=Case(
...: When(answers__in=user.answers.filter(is_agreed=True), then=Value(True)),
...: When(answers__in=user.answers.filter(is_agreed=False), then=Value(False)),
...: default=Value(None),
...: output_field=NullBooleanField(),
...: ),
...: ).annotate(answers_amount=Count('answers'))
...: qs.count()
Out[19]: 4
^ здесь 4, но у меня есть только 3 вопроса в БД :(
Итак, я попробовал с distinct()
In [20]: qs.distinct().count()
Out[20]: 4 # but distinct does not work
In [21]: qs.distinct('id').count()
И после последней строки кода у меня есть это исключение:
NotImplementedError: annotate() + distinct(fields) is not implemented.
Я также пытался использовать этот трюк annotate(Count('id')).filter(id__count__gt=1)
Но в этом случае я теряю все повторяющиеся строки, и qs.count() равен 2.
ОБНОВЛЕНИЕ: Проблема заключается в дублировании строк в наборе запросов.
РЕШЕНИЕ: (расширенный вариант второго подхода Владимира)
user = User.objects.first()
user_agreed_questions = user.answers.filter(
is_agreed=True).values_list('question_id', flat=True)
user_not_agreed_questions = user.answers.filter(
is_agreed=False).values_list('question_id', flat=True)
Question.objects.annotate(
answer_amount=Count('answers'),
is_user_agreed=Case(
When(id__in=user_agreed_questions, then=True),
When(id__in=user_not_agreed_questions, then=False),
default=None,
output_field=NullBooleanField(),
),
)