Django странное поведение агрегации над настраиваемым полем ManyToMany - PullRequest
0 голосов
/ 26 июня 2018

Я столкнулся с неожиданным (для меня) поведением агрегации над полем 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 соответствующего конкурса. Это где-то задокументировано? Я ничего не смог найти.

1 Ответ

0 голосов
/ 27 июня 2018

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

Task.objects.get(id=1)

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

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

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

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

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

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

...