Доступ к Django через дополнительные поля модели в обработчике сигнала m2m_changed - PullRequest
0 голосов
/ 28 февраля 2020

Допустим, у меня есть следующие Django модели, которые представляют отсортированные отношения между Родителем и Дочерним:

class Parent(models.Model):
    name = models.CharField(max_length=50)
    children = models.ManyToManyField("Child", through="ParentChild")

class Child(models.Model):
    name = models.CharField(max_length=50)

class ParentChild(models.Model):
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["parent", "child"], name="uc_parent_child"),
            models.UniqueConstraint(fields=["parent", "sort_number"], name="uc_parent_child"),
        ]

    parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
    child = models.ForeignKey(Child, on_delete=models.CASCADE)
    sort_number = models.IntegerField()

    def save(self, *args, **kwargs):
        exising_sort_numbers = self.parent.parentchild_set.values_list(
            "sort_number", flat=True
        )
        if self.sort_number in exising_sort_numbers:
            raise Exception(f"Duplicate sort number: {self.sort_number}")
        super().save(*args, **kwargs)

Теперь, если я создаю отношения с использованием сквозной модели, я получаю исключение для дубликат sort_number:

ParentChild.objects.create(parent=parent, child=child1, sort_number=0)
ParentChild.objects.create(parent=parent, child=child2, sort_number=0)  # raises Exception

Однако, если я создаю отношения, используя метод .add, я не получу исключение:

parent.children.add(child1, through_defaults={"sort_number": 0})
parent.children.add(child2, through_defaults={"sort_number": 0})  # does NOT raise Exception

Я знаю использование метода .add не вызывает метод .save на сквозной модели , поэтому мне нужно использовать сигнал m2m_change для запуска этой логики c. Но я не уверен, как получить sort_number в этом сигнале. Вот код, который у меня есть для сигнала на данный момент:

@receiver(m2m_changed, sender=Parent.children.through)
def validate_something(sender, instance, action, reverse, model, pk_set, **kwargs):
    if action == "pre_add":
        for pk in pk_set:
            child = model.objects.get(pk=pk)
            exising_sort_numbers = instance.parentchild_set.values_list(
                "sort_number", flat=True
            )
            # where's sort_number specified in through_defaults ???

Есть идеи, как я могу получить это значение и выполнить проверку "pre_add", или это невозможно?

1 Ответ

0 голосов
/ 28 февраля 2020

У вас есть это ограничение - models.UniqueConstraint(fields=["parent", "sort_number"], name="uc_parent_child"), что означает, что вы не можете иметь более одного отношения с одинаковыми parent и sort_number. Есть даже дополнительная проверка в методе ParentChild save, чтобы усилить это. Имеет смысл выдавать исключение при попытке создать такое отношение.

Кроме того, имя ограничения должно быть уникальным. Я попробовал код и не смог выполнить миграцию как есть.

Если вы сделаете то, что пытаетесь, вы получите это исключение снова при сохранении. Вместо того, чтобы пытаться обойти ограничение, вы должны либо изменить / удалить его, либо адаптировать свой код для работы с ним - не пытайтесь создать экземпляр, который будет его нарушать.

Что касается вашего указанного c вопрос, экземпляр, который вы получаете в validate_something - это Parent, и нет прямого доступа к экземпляру-посреднику или по умолчанию. Вы также не можете запросить экземпляр-посредник, потому что он еще не существует.

...