Как обработать сигнал после сохранения для отношений M2M с наследованием модели в Django? - PullRequest
1 голос
/ 04 июня 2019

Заголовок вопроса - вполне предложение, но, надеюсь, приведенный ниже код прояснит его:

class Foo(models.Model):
    ...


class AbstractParent(models.Model):
    foos = models.ManyToManyField(
        Foo,
        related_name='%(app_label)s_%(class)s_related'
    )

    def bar(self):
        ...

    class Meta:
        abstract = True


class ChildOne(AbstractParent):
    ...


class ChildTwo(AbstractParent):
    ...

Допустим, ярлык моего приложения - myapp.

В основном, базовый класс ChildOne и ChildTwo имеет M2M для класса Foo. Я хочу сделать следующее: всякий раз, когда объект класса Foo сохраняется, я хочу вызывать метод bar() всех объектов ClassOne и ClassTwo, который имеет отношение к Foo объект через поле foos. Для этого я попытался написать простой сигнал:

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=Foo)
def call_bar_for_all_related_objects_to_foo(sender, instance, **kwargs):
    # Do the thing

В этот момент я вроде как потерян. Как запросить все дочерние классы класса AbstractParent и вызывать их методы bar() всякий раз, когда в сигнале есть связь с instance? В идеале я хочу сделать запрос к моей базе данных только один раз, а в одном запросе я хочу получить все объекты ChildOne и ChildTwo, связанные с instance в сигнале. Обратите внимание, что в моих реальных моделях есть более двух дочерних классов AbstractParent, поэтому ответ, который учитывает это, приветствуется. Спасибо за любую помощь.

1 Ответ

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

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

def call_bar_for_all_related_objects_to_foo(sender, instance, **kwargs):
    for field in instance._meta.get_fields():
        if not field.related_model:
            continue
        if not issubclass(field.related_model, AbstractParent):
            continue
        for related_object in getattr(instance, field.related_name).all():
            related_object.bar()

Обновление одного запроса

Я не думаю, что это можно сделать в одном запросе, как обычно. Единственный известный мне способ получить все связанные объекты django из одного запроса - это select_related, который не работает с ManyToMany отношениями.

Если важен один запрос, для реализации, вероятно, потребуются более конкретные сведения о .bar(), которые, вероятно, потребуется преобразовать в метод класса, который работает с результатами вызова .values() или чем-то подобным.

...