在Django中如何在外键关系中保存子对象时通知父对象?

18 投票
3 回答
4290 浏览
提问于 2025-04-11 09:30

我有以下两个模型:

class Activity(models.Model):
    name = models.CharField(max_length=50, help_text='Some help.')
    entity = models.ForeignKey(CancellationEntity)
    ...


class Cancellation(models.Model):
    activity = models.ForeignKey(Activity)
    date = models.DateField(default=datetime.now().date())
    description = models.CharField(max_length=250)
    ...

我希望活动模型能够知道与它相关的取消操作何时被保存(无论是新增还是更新)。

有什么好的方法可以实现这个吗?

3 个回答

0

在Django中,你可以使用一个叫做信号的东西,当子对象发生变化时更新父对象。为了做到这一点,你需要创建一个信号,它会监听子模型的post_save信号,并发送一个信号来更新父模型。

# models.py

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


class Activity(models.Model):
    name = models.CharField(max_length=50, help_text='Some help.')
    entity = models.ForeignKey(CancellationEntity)


class Cancellation(models.Model):
    activity = models.ForeignKey(Activity)
    date = models.DateField(default=datetime.now().date())
    description = models.CharField(max_length=250)

@receiver(post_save, sender=Cancellation)
def update_activity(sender, instance, **kwargs):
    activity= instance.activity
    activity.save()

在上面的代码中,我们创建了一个信号函数,用来监听Cancellation模型的post_save信号。update_activity函数接收两个参数:senderinstance。其中,sender是发送信号的模型类,而instance是被保存的模型实例。kwargs参数则包含任何额外的关键字参数。

当一个Cancellation对象被保存时,sender参数会被设置为Cancellation,而instance参数会被设置为刚刚保存的Cancellation实例。update_activity函数会处理activity对象并保存它,如果有的话,会触发它的post_save信号。

所以,现在每当一个Cancellation对象被保存时,update_activity函数也会更新Activity模型。

5

下面的代码有什么问题呢?

class Cancellation( models.Model ):
    blah
    blah
    def save( self, **kw ):
        for a in self.activity_set.all():
            a.somethingChanged( self )
        super( Cancellation, self ).save( **kw )

这段代码可以让你非常精确地控制不同模型之间的通知。 从某种意义上说,这就是经典的“面向对象编程(OO)为什么这么好?”的问题。我认为面向对象编程之所以好,正是因为你可以让取消和活动这些对象之间完全合作。

18

你需要关注的是 Django 的信号(也可以看看 这个页面),特别是模型信号,更具体地说是 post_save 信号。信号是 Django 提供的一种插件或钩子系统。每当一个模型被保存时,不管是更新还是新建,post_save 信号就会被发送(而且它会告诉你这个模型是新建的)。这就是你如何使用信号来接收通知,当一个活动被取消时。

from django.db.models.signals import post_save

class Activity(models.Model):
    name = models.CharField(max_length=50, help_text='Some help.')
    entity = models.ForeignKey(CancellationEntity)

    @classmethod
    def cancellation_occurred (sender, instance, created, raw):
        # grab the current instance of Activity
        self = instance.activity_set.all()[0]
        # do something
    ...


class Cancellation(models.Model):
    activity = models.ForeignKey(Activity)
    date = models.DateField(default=datetime.now().date())
    description = models.CharField(max_length=250)
    ...

post_save.connect(Activity.cancellation_occurred, sender=Cancellation)

撰写回答