Django的NotImplementedError:未实现annotate()+ distinct(fields)

Mar*_*hyn 12 python django postgresql

有2个简单的模型:

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')
Run Code Online (Sandbox Code Playgroud)

现在,我将描述我的问题.

In [18]: Question.objects.count()
Out[18]: 3
Run Code Online (Sandbox Code Playgroud)

我需要使用'is_user_agreed'和'answers_amount'字段来注释queryset:

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 
Run Code Online (Sandbox Code Playgroud)

^这里的数量是4,但我在db中只有3个问题 :(所以,我试过了distinct()

In [20]: qs.distinct().count()
Out[20]: 4  # but distinct doesn't work

In [21]: qs.distinct('id').count()
Run Code Online (Sandbox Code Playgroud)

在最后一行代码之后我得到了这个例外:

NotImplementedError: annotate() + distinct(fields) is not implemented.
Run Code Online (Sandbox Code Playgroud)

我也试过用这个技巧 annotate(Count('id')).filter(id__count__gt=1)

但在这种情况下,我丢失了所有重复的行,而qs.count()是2.

更新:问题是queryset中的重复行.

解决方案:(弗拉基米尔第二种方法的扩展变体)

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(),
    ),
)
Run Code Online (Sandbox Code Playgroud)

Vla*_*lov 8

试试这个:

Question.objects.annotate(
    answer_amount=Count('answers'),
    is_user_agreed=F('answers__is_agreed'),
).order_by('id', '-answers__is_agreed').distinct('id')
Run Code Online (Sandbox Code Playgroud)

如果question没有answers,那question.is_user_agreed就是None.如果question有至少一个answeranswer.is_agreed=True,然后question.is_agreedTrue.否则is_user_agreed就是False.

或这个:

agreed_questons = Answer.objects.filter(
    is_agreed=True,
).values_list('question_id', flat=True).distinct()

Question.objects.annotate(
    answer_amount=Count('answers'),
    is_agreed=Case(
        When(id__in=agreed_questions, then=True),
        When(answers__isnull=True, then=None),
        default=False,
        output_field=NullBooleanField(),
    ),
)
Run Code Online (Sandbox Code Playgroud)

agreed_questons是的名单idquestions,有至少一个answeranswer.is_agreed=True.

  • 我的注释运行良好,主要问题是结果查询集中的重复条目。 (2认同)
  • 我已经检查了你的第二种方法,它对我有用(有问题的细节)。非常感谢! (2认同)