Django:如何在 post_save 信号中访问原始(未修改)实例
Posted
技术标签:
【中文标题】Django:如何在 post_save 信号中访问原始(未修改)实例【英文标题】:Django: How to access original (unmodified) instance in post_save signal 【发布时间】:2011-07-31 17:51:39 【问题描述】:我想进行数据非规范化以获得更好的性能,并将我的博客文章收到的投票总和放入 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 信号中有类似的东西吗?
更新
基于马克的答案的解决方案:
# 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()
【问题讨论】:
【参考方案1】:我认为post_save
检索未修改版本为时已晚。顾名思义,此时数据已经写入数据库。您应该改用pre_save
。在这种情况下,您可以通过 pk:old = Vote.objects.get(pk=instance.pk)
从数据库中检索模型并检查当前实例和前一个实例的差异。
【讨论】:
【参考方案2】:这不是最佳解决方案,但它确实有效。
@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
【讨论】:
为什么不是最优的? @CharanjitSingh 它有效,但我不确定这是正确的方法。 是的,据我所知,它可能是最佳的,但预测性较差,因为旧实例可以从内存中删除,我不确定 django 的信号工作流程,但这可能成为一个问题。 我只建议不要保存整个实例,只需将您需要比较的数据子集存储在 post_save 中。【参考方案3】:您可以使用 django-model-utils 中的 FieldTracker:https://django-model-utils.readthedocs.io/en/latest/utilities.html#field-tracker
【讨论】:
以上是关于Django:如何在 post_save 信号中访问原始(未修改)实例的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Django 信号(Pre_save,Post_save)从“B”模型的 ImageField 设置“A”模型的 ImageField
Django 从 post_save 信号访问 ManyToMany 字段