0

I've encountered some unexpected (for me) behavior of aggregation over ManyToMany field in django.

I have the following schema:

class ContestTaskRelationship(models.Model):
    contest = models.ForeignKey('Contest', on_delete=models.CASCADE)
    task = models.ForeignKey('Task', on_delete=models.CASCADE)
    solved = models.ManyToManyField('User', related_name='contests_tasks_solved', blank=True)
    cost = models.IntegerField(default=0)

class Contest(models.Model):
    tasks = models.ManyToManyField('Task',
                                   related_name='contests',
                                   blank=True,
                                   through='ContestTaskRelationship')

class Task(models.Model):
    pass

Then I have one task and two contests, connected to that task. If I try to annotate count of ContestTaskRelationship models like that (assuming contest is one of the contests):

task = contest.tasks.annotate(number_solved=Count('contesttaskrelationship')).first()

I get task.number_solved == 1, but when I try like that:

task = Task.objects.filter(id=1).annotate(number_solved=Count('contesttaskrelationship')).first()

I get the expected result task.number_solved == 2. Why is that? Isn't it the same object and the same field?

UPD: I found out that in the first example additional condition is added to query, the contest_id is compared to the id of corresponding contest. Is it documented somewhere? I couldn't find anything.

pomo_mondreganto
  • 2,028
  • 2
  • 28
  • 56

1 Answers1

-1

When you write this code:

Task.objects.get(id=1)

you will get the Task instance, and it seems that there is method named 'annotate' with 'number_solved' argument in your Task model. You cannot do any annotate after some queryset methods: get, count, aggregate, last, first, exists and may be some more. This methods do not return queryset.

If you want to see the query, and compare it.

You can do two things:

  1. print(Contest.objects.all().query) # return raw query to db
  2. install django-extensions and use this command to add shell:

    ~# python manage.py shell_plus --print-sql  
    

if you see the query you can compare it and solve this problem by yourself

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Andrei Berenda
  • 1,946
  • 2
  • 13
  • 27