Save() сохраняет все поля, кроме поля ManyToMany

У меня есть модель "Contest" с одним полем m2m под названием "teams", которое связано с моделью "Team". Я переопределил метод save. В моей функции save() (та, которая переопределяется) мне нужен queryset (в моей переопределяющей функции save) со всеми объектами, связанными с моим m2m-полем team. Код self.teams.all(), но он не работает, потому что мои модели еще не зарегистрированы в базе данных, правильно? Итак, я вызываю super().save(*args, **kwargs) теперь мои модели сохранены и я могу получить свой набор запросов? Я не могу. Кверисет пуст, даже если я зарегистрировал команду(ы). <QuerySet []> Почему super.save() сразу сохраняет все поля, кроме m2m ? Для создания моделей я использую исключительно админку django. Никакой оболочки manage.py или form.

Моя модель :

class Contest(models.Model):
name = models.CharField(max_length=16, primary_key=True, unique=True, default="InactiveContest", blank=True)  # Ex: PSGvMNC_09/2017
id = models.IntegerField(default=1)
teams = models.ManyToManyField(Team, verbose_name="opposants")
date = models.DateTimeField(blank=True)
winner = models.ForeignKey(Team, verbose_name='gagnant', related_name='winner', on_delete=models.SET_NULL, blank=True, null=True)
loser = models.ForeignKey(Team, verbose_name='perdant', related_name='loser', on_delete=models.SET_NULL, blank=True, null=True)
bet = models.IntegerField(verbose_name='Nombre de paris', default=0, blank=True, null=0)
active = models.BooleanField(default=False)

def save(self, *args, **kwargs):
    if self._state.adding:
        self.active = False
        # Django's id field immitation
        last_id = Contest.objects.all().aggregate(Max('id')).get('id__max')
        if last_id is not None:
            self.id = last_id + 1
    super().save(*args, **kwargs)
print(Contest.objects.get(id=self.id)) # Works --> model saved in db... in theory
    queryset = self.teams.all() # Empty all the time !

После того, как save() (переопределяющий) был выполнен один раз, проблема решена и в следующие разы (модификации) я могу получить свой queryset, поэтому я мог бы просто использовать self._state.adding, но этот метод обязывает меня сохранять 2 раза (создание, редактирование). Мне нужно понять, почему super().save(*args, **kwargs) ведет себя так и как я могу решить эту проблему?

Причина в том, что реализация ManyToManyField не использует таблицу базы данных модели. Она использует третью таблицу для связи между двумя моделями.

Каждая модель имеет свою собственную таблицу базы данных. В вашем примере

model Contest -> table app_contest
model Team   -> table app_team

Однако, app_contest не содержит поля teams. А app_team не содержит поля contest.

Вместо этого существует третья таблица, например, app_contest_team, которая состоит из трех столбцов:

  • id
  • contest_id
  • team_id

И хранит пары contest_id + team_id, которые представляют связь между конкурсами и командами.

Так что queryset = self.teams.all() равно SQL выражению:

SELECT * FROM app_teams WHERE id in 
    (SELECT team_id from app_contest_teams WHERE contest_id = '{self.id}');

Таким образом, видно, что вы не можете каким-либо образом манипулировать ManyToManyField без наличия экземпляра, первоначально сохраненного в базе данных. Потому что только после вставки в таблицу app_contest он получает уникальный ID, который в дальнейшем может быть использован для создания записей в третьей таблице app_contest_teams.

А также из-за этого - save() не требуется при добавлении нового объекта в ManyToManyField, потому что таблица модели не затрагивается

Вернуться на верх