Странное поведение агрегации Django по пользовательскому полю ManyToMany

Я столкнулся с неожиданным (для меня) поведением агрегации по полю ManyToMany в django.

У меня есть следующая схема:

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

Затем у меня есть одно задание и два конкурса, связанных с этим заданием. Если я попытаюсь аннотировать количество ContestTaskRelationship моделей таким образом (при условии, что contest является одним из конкурсов):

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

Я получаю task.number_solved == 1, но когда я пытаюсь так:

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

Я получаю ожидаемый результат task.number_solved == 2. Это почему? Разве это не тот же объект и то же поле?

UPD: Обнаружил, что в первом примере к запросу добавляется дополнительное условие, contest_id сравнивается с id соответствующего конкурса. Это где-то задокументировано? Я ничего не мог найти.


person pomo_mondreganto    schedule 26.06.2018    source источник


Ответы (1)


Когда вы пишете этот код:

Task.objects.get(id=1)

вы получите экземпляр Task, и кажется, что в вашей модели Task есть метод с именем «annotate» с аргументом «number_solved». Вы не можете делать какие-либо аннотации после некоторых методов набора запросов: получить, подсчитать, агрегировать, последний, первый, существует и может быть еще несколько. Эти методы не возвращают набор запросов.

Если вы хотите увидеть запрос, и сравнить его.

Вы можете сделать две вещи:

  1. print(Contest.objects.all().query) # вернуть необработанный запрос в БД
  2. установите django-extensions и используйте эту команду для добавления оболочки:

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

если вы видите запрос, вы можете сравнить его и решить эту проблему самостоятельно

person Andrey Berenda    schedule 26.06.2018