【问题标题】:How to handle a post-save signal for an M2M relationship with model inheritance in Django?如何在 Django 中处理具有模型继承的 M2M 关系的保存后信号?
【发布时间】:2019-10-19 12:38:57
【问题描述】:

问题标题就是一句话,但希望下面的代码能把它弄清楚:

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”。

基本上,ChildOneChildTwo 的基类与Foo 类具有M2M。我想做的是:每当保存Foo 类的对象时,我想调用ClassOneClassTwo 的所有对象的bar() 方法,这与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 类的所有子类并在信号中与instance 相关时调用它们的bar() 方法?理想情况下,我只想查询我的数据库一次,并且在一次查询中,我想获取与信号中的instance 相关的ChildOneChildTwo 的所有对象。请注意,在我的实际模型中,AbstractParent 有两个以上的子类,因此请记住这一点的答案。感谢您的帮助。

【问题讨论】:

    标签: django django-models many-to-many django-signals


    【解决方案1】:

    嗯,它不能满足您的单个查询要求,但这里有一种方法至少可以完成每个子类的查询:

    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() 调用或其他东西的结果。

    【讨论】:

    • 有什么方法可以在一个数据库查询中执行?
    • 更新了更多细节,但简短的回答:我不知道
    • 很公平。这是一个复杂的问题,这是我所知道的最好的实现。非常感谢。
    • 很抱歉再次提出这个问题,但在测试代码后,我收到以下错误:TypeError: 'ManyRelatedManager' object is not iterable, in line for related_object in getattr(instance, field.related_name):
    • 对不起,我离开 .all() 以使查询集可迭代。现已修复。
    猜你喜欢
    • 2017-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多