Как сделать сигнал m2m_changed атомарным в Django? - PullRequest
0 голосов
/ 01 июня 2019

В моем проекте есть следующие модели:

class Topping(models.Model):
    name = models.CharField(max_length=255)

class Pizza(models.Model):
    name = models.CharField(max_length=255)
    toppings = models.ManyToManyField(Topping, blank=True)

    def save(self, *args, **kwargs):
        print('Saving...')
        if self.pk:
            for topping in self.toppings.all():
                print(topping.name)

        super(Pizza, self).save(*args, **kwargs)

def toppings_changed(sender, instance, **kwargs):
    instance.save()

m2m_changed.connect(toppings_changed, sender=Pizza.toppings.through)

Как правило, при изменении toppings сигнал срабатывает. Все, что делает сигнал - это вызывает метод сохранения объекта Pizza. В любом случае, допустим, у меня есть три объекта:

pizza = Pizza.objects.get(pk=1) # Number of toppings is 0
topping1 = Topping.objects.get(pk=1)
topping2 = Topping.objects.get(pk=2)

Теперь я хочу установить две начинки для моей пиццы. Я делаю это, используя следующий код:

pizza = Pizza.objects.get(pk=1)
pizza.toppings.set([1, 2])

Начинки установлены правильно, однако метод сохранения пиццы вызывается дважды, потому что сигнал m2m_changed вызывается дважды, поскольку происходят два изменения. Как я могу назвать это только один раз после того, как все изменения были зафиксированы? Чтобы уточнить, я хочу, чтобы обе начинки были добавлены, но я хочу подать свой сигнал только один раз, в конце всех изменений. Спасибо за любую помощь.

1 Ответ

1 голос
/ 03 июня 2019

Сигнал m2m_changed несколько странный, так как он вызывается дважды: один раз с действием pre_add и один раз с действием post_add (см. документы ).Так как вы вызываете toppings_changed() для любого действия, оно будет вызвано дважды, следовательно, save() будет вызвано дважды.

Похоже, вы заинтересованы в post_add, поэтому я бысделайте что-то вроде:

@receiver(m2m_changed, sender=Pizza.toppings.through)
def toppings_changed(sender, instance, action, **kwargs):
    if action == "post_add":
        instance.save()

Здесь instance.save() следует вызывать только один раз, независимо от того, сколько аргументов вы передаете set().Обратите внимание, что вам не нужно будет делать m2m_changed.connect(...), если вы используете декоратор приемника.

Вышесказанное также относится к pre/post_remove и pre/post_clear, поэтому, если вы хотите распечатать свой список после удаления,вам нужно будет добавить еще немного логики, чтобы проверить, если action == "post_remove".

Наконец, на всякий случай, если вы не знаете, set([1, 2]) заменит все ваши начинки двумяВы предоставили (pk=1 и pk=2).Если вы просто хотите добавить начинки к существующему набору, я бы использовал add(1, 2).

Надеюсь, это поможет!

...