Django信号模型值变化条件?

时间:2016-08-19 08:48:48

标签: python django signals

我的models.py中有一个非常简单的模型,如下所示:

class Music(models.Model):
    title= models.CharField(
        max_length=100
    )

    description = models.TextField(
        null=True,
        blank=True
    )

    def __unicode__(self):
        return self.name


class Album(models.Model):
    musician= models.CharField(
        related_name='musician',
        max_length=100
    )

    music = models.ForeignKey(
        Music,
        related_name='music'
    )

    def __unicode__(self):
        return self.user.username

在这种情况下,我在signals.py中创建了一个非常简单的逻辑来检查新字段(新音乐)是否刚刚添加到相册中。

Album(musician="someone", music=Music.objects.get(title="something")) #pk=1
Album.save()
像这样:

@receiver(post_save, sender=Album)
def add_new_album(sender, instance, **kwargs):   
    if kwargs.get('created'):
       print "new album recently created"

在其他情况下,我想制作一个仅在现有字段编辑为新字段时才会响应的信号:

edit_album = Album.objects.get(pk=1)
edit_album.music = Music.objects.get(title="something_else")
edit_album.save()

使用post_save接收器使每次编辑现有字段时都会触发add_new_album()函数。所以我的问题是,在仅响应编辑字段条件的signals.py中应该实现的逻辑是什么? / p>

2 个答案:

答案 0 :(得分:0)

所以我想只在某个字段改变时才触发你的信号? 您可以使用名为Django Field history

的包

这样的事情:

@receiver(post_save, sender=<sender>, dispatch_uid=<string>)
def method(sender, instance, **kwargs):
    last_field_change = FieldHistory.objects.filter(field_name=<your_field_which_you_want_to_check_for_change>,
                                          object_id=instance.id).last()
    if last_field_change:
         #do your thing

答案 1 :(得分:0)

我知道这是一个古老的问题,但是我最近遇到了这个特定问题,我想分享我的最初解决方案,以防它对其他人甚至是原始海报有帮助。或者更好的是,激发讨论以带来更好的解决方案。

我不想使用任何第三方库(尤其是在DB上进行了迁移的第三方库),并希望使用Django提供的内置信号。

我的解决方法是:

1)创建要在特定字段发生更改时触发的函数(在发问者的情况下,专辑实例上的音乐字段)

def do_this_when_album_music_changed(sender, **kwargs):
    // do something
    print("I'm only going to be called if the field has changed")

2)创建要在特定字段更改时发送的自定义信号

album_music_changed = Signal(providing_args=[])

3)创建一个pre_save信号接收器,该信号接收器将检查字段是否已更改,然后将自定义信号“连接”到接收器,否则“断开”连接

@receiver(signal=signal=models.signals.pre_save, sender=Album)
def album_pre_save(sender, instance, **kwargs):
    old_album = sender.objects.get(pk=instance.pk)
    if not old_album.music == instance.music:
        album_music_changed.connect(do_this_when_album_music_changed, sender=Album)
    else:
        album_music_changed.disconnect(do_this_when_album_music_changed, sender=Album)

4)最后添加一个post_save信号,该信号实际上将发送触发器以调用自定义信号,并因此发送您要运行的功能

@receiver(signal=signal=models.signals.post_save, sender=Album)
def album_post_save(sender, instance, **kwargs):
    album_music_changed.send(sender=sender)

就我而言,我需要在实例的post_save之后运行该函数,因此,如果有人有更好的解决方案,如果您可以共享它,那将是我能想到的最好的事情。

当然,如果您不需要触发函数来运行post_save并且很乐意在pre_save上运行它,则只需在第一个“ if”块内调用.send()即可,而不必担心post_save接收器

希望这会有所帮助