Есть ли способ сохранить объект Django и обновить другие объекты без рекурсивного цикла?

У меня есть модель Django:

class Event(models.Model):
   fk = models.ForeignKey(Foreign, null=True, on_delete=models.SET_NULL)
   display = display = models.BooleanField(default=True)
   ...

Я хотел бы переопределить метод сохранения, чтобы отключить отображение для других событий, имеющих общее значение fk. Однако я продолжаю достигать бесконечных циклов рекурсии, потому что если я переопределяю метод сохранения, а затем сохраняю другие объекты, он продолжает вызывать функцию. Есть ли способ запустить метод сохранения только для первого сохраняемого объекта и не создавать рекурсивные экземпляры?

Вы можете работать .update(…) [Django-doc] с QuerySet другими объектами, с:

from django.db.models import Q

class Event(models.Model):
    fk = models.ForeignKey(Foreign, null=True, on_delete=models.SET_NULL)
    display = models.BooleanField(default=True)
    # …

    def save(self, *args, **kwargs):
        if self.fk_id is not None and self.display:
            Event.objects.filter(
                ~Q(pk=self.pk), fk_id=self.fk_id
            ).update(display=False)
        return super().save(*args, **kwargs)

Но это также сразу показывает, что есть способы обойти .save(…) метод [Django-doc] на модели, поэтому простого переопределения .save(…) будет не достаточно, поскольку существуют методы ORM, которые обходят .save(…) и сигналы pre-save и post-save (которые вызываются методом .save(…)).

Вы также можете добавить ограничение для обеспечения того, что существует не более одного Event для каждого fk, который имеет display=True:

from django.db.models import Q

class Event(models.Model):
    fk = models.ForeignKey(Foreign, null=True, on_delete=models.SET_NULL)
    display = models.BooleanField(default=True)
    # …

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=('fk',),
                condition=Q(display=True),
                name='only_one_display_per_fk'
            )
        ]
Вернуться на верх