Django 在 update() 上触发 post_save

问题描述 投票:0回答:3

所以我使用 django-simple-history 模块来跟踪模型实例的变化。但是,它使用 post_save 信号来执行此操作。在我的项目中,我还需要它在 update() 上触发。

我的问题是:如何覆盖 update() 方法来触发 post_save 信号?

python django django-models django-rest-framework django-simple-history
3个回答
19
投票

这样做的问题是,

.update()
不需要从数据库加载对象来完成它的任务。考虑一下具有 100 万行的 users 表的示例:

users = User.objects.filter(date_joined__lte=now() - timedelta(days=30)
users.update(is_active=False)

django 在这里要做的不是从数据库中加载潜在的数十万行,只是将

is_active
设置为 False,然后单独保存每一行,而是直接通过数据库引擎发出
UPDATE
命令:
UPDATE users SET is_active=False WHERE date_joined < 30_DAYS_AGO 
。这是 Django 在更新时不触发
post_save
的唯一原因:因为它一开始就没有从数据库加载任何内容。

为了触发信号,它需要从数据库加载所有这些对象,发出数据库查询

UPDATE users SET is_active=False WHERE id=X
数千次,每行一次,然后发送信号。这会降低性能。

如果您确实想使用该信号,则需要从数据库加载对象,迭代它们并一次保存一个。不幸的是,没有办法解决这个问题。


0
投票

我们可以重写方法并手动调用 post_save :

class CustomQuerySet(models.QuerySet):
    def update(self, **kwargs):
        # Get instances before the update (this will fetch objects from DB)
        instance_list = list(self)
        
        # Perform the update operation
        updated_count = super().update(**kwargs)
        
        # Send post_save signal manually for each instance
        for instance in instance_list:
            for key, value in kwargs.items():
                setattr(instance, key, value)
            post_save.send(sender=self.model, instance=instance, created=False)
        
        return updated_count

    def bulk_create(self, objs, **kwargs):
        # Send pre_save signal for each instance
        for obj in objs:
            pre_save.send(sender=self.model, instance=obj)
        
        # Perform bulk_create
        created_objs = super().bulk_create(objs, **kwargs)
        
        # Send post_save signal for each created instance
        for obj in created_objs:
            post_save.send(sender=self.model, instance=obj, created=True)
        
        return created_objs

    def bulk_update(self, objs, fields, **kwargs):
        # Send pre_save signal for each instance
        for obj in objs:
            pre_save.send(sender=self.model, instance=obj)
        
        # Perform bulk_update
        result = super().bulk_update(objs, fields, **kwargs)
        
        # Send post_save signal for each instance
        for obj in objs:
            post_save.send(sender=self.model, instance=obj, created=False)

        return result



class SignalManager(models.Manager):
    def get_queryset(self):
        return CustomQuerySet(self.model, using=self._db)

然后在模型中使用 SignalManger 如下:

class MyModel(models.Model):
    objects = SignalManager()

-5
投票

save() 方法在 Django 模型对象创建或更新时被调用。

例如: 创建新的用户配置文件, 更新现有的用户个人资料。

对于这两种场景,我们可以使用Django信号发送邮件来通知用户。

为此,我们使用 Django 内置信号 post_save。

只要在 Django 模型实例上调用 .save() ,就会调度 post_signal 。

我们可以在 models.py 中使用以下代码:

from django.db.models import signals
from django.dispatch import receiver
@receiver(signals.post_save, sender=Customer)
def on_create_or_updated_obj(sender, instance, **kwargs):
   if kwargs['created']:
      print 'obj created'
      #send user created email to user
   else:
      print 'obj updated'
      #logic for sending user updated email to user
© www.soinside.com 2019 - 2024. All rights reserved.