Django:如何在post_save信号中访问原始实例
我想对数据进行去规范化,以提高性能,并把我博客文章收到的投票总数放在 Post 模型里面:
class Post(models.Model):
""" Blog entry """
author = models.ForeignKey(User)
title = models.CharField(max_length=255)
text = models.TextField()
rating = models.IntegerField(default=0) # here is the sum of votes!
class Vote(models.Model):
""" Vote for blog entry """
post = models.ForeignKey(Post)
voter = models.ForeignKey(User)
value = models.IntegerField()
当然,我需要保持 Post.rating
的值是最新的。通常我会使用数据库触发器来做到这一点,但现在我决定使用 post_save
信号(这样可以减少数据库处理时间):
# vote was saved
@receiver(post_save, sender=Vote)
def update_post_votes(sender, instance, created, **kwargs):
""" Update post rating """
if created:
instance.post.rating += instance.value
instance.post.save()
else:
# if vote was updated, we need to remove the old vote value and add the new one
# but how...?
我该如何在保存之前访问实例的值呢?在数据库触发器中,我会有 OLD
和 NEW
这样的预定义值,但在 post_save 信号中有没有类似的东西呢?
更新
这个解决方案是基于 Mark 的回答:
# vote was saved
@receiver(pre_save, sender=Vote)
def update_post_votes_on_save(sender, instance, **kwargs):
""" Update post rating """
# if vote is being updated, then we must remove previous value first
if instance.id:
old_vote = Vote.objects.get(pk=instance.id)
instance.post.rating -= old_vote.value
# now adding the new vote
instance.post.rating += instance.value
instance.post.save()
3 个回答
2
你可以使用django-model-utils里的FieldTracker,具体可以查看这个链接:https://django-model-utils.readthedocs.io/en/latest/utilities.html#field-tracker
24
这不是一个最优的解决方案,但它可以正常工作。
@receiver(pre_save, sender=SomeModel)
def model_pre_save(sender, instance, **kwargs):
try:
instance._pre_save_instance = SomeModel.objects.get(pk=instance.pk)
except SomeModel.DoesNotExist:
instance._pre_save_instance = instance
@receiver(signal=post_save, sender=SomeModel)
def model_post_save(sender, instance, created, **kwargs):
pre_save_instance = instance._pre_save_instance
post_save_instance = instance
75
我觉得 post_save
这个时机太晚了,无法获取未修改的版本。因为它的名字就说明了,这时候数据已经写入数据库了。你应该使用 pre_save
。在这个时机,你可以通过主键从数据库中获取模型:old = Vote.objects.get(pk=instance.pk)
,然后对比当前的实例和之前的实例,看看有什么不同。