Django: NotImplementedError: annotate () + Different (fields) not implemented

There are two simple models:

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') 

And there is my problem:

 In [18]: Question.objects.count() Out[18]: 3 

I need to annotate a set of queries using the is_user_agreed and answers_amount fields:

 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 

^ here 4, but I have only 3 questions in the DB :( So, I tried with distinct()

 In [20]: qs.distinct().count() Out[20]: 4 # but distinct does not work In [21]: qs.distinct('id').count() 

And after the last line of code, I have this exception:

 NotImplementedError: annotate() + distinct(fields) is not implemented. 

I also tried using this trick annotate(Count('id')).filter(id__count__gt=1)

But in this case, I lose all duplicate rows, and qs.count () is 2.

UPDATE: The problem is duplicating rows in the query set.

SOLUTION: (an expanded version of Vladimir’s second approach)

 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(), ), ) 
+12
source share
1 answer

Try the following:

 Question.objects.annotate( answer_amount=Count('answers'), is_user_agreed=F('answers__is_agreed'), ).order_by('id', '-answers__is_agreed').distinct('id') 

If question has no answers , then question.is_user_agreed is None . If question has at least one answer with answer.is_agreed=True , then question.is_agreed is True . Otherwise, is_user_agreed False .

Or that:

 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(), ), ) 

agreed_questons is an id of questions list that has at least one answer with answer.is_agreed=True .

+8
source

All Articles