识别 django post_save 信号中更改的字段
Identify the changed fields in django post_save signal
我正在使用 django 的 post_save 信号在保存模型后执行一些语句。
class Mode(models.Model):
name = models.CharField(max_length=5)
mode = models.BooleanField()
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
# do some stuff
pass
现在我想根据mode
字段的值是否发生变化来执行一条语句。
@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
# if value of `mode` has changed:
# then do this
# else:
# do that
pass
我查看了一些 SOF 线程和博客,但找不到解决方案。他们都试图使用 pre_save 方法或表单,这不是我的用例。 https://docs.djangoproject.com/es/1.9/ref/signals/#post-save 在 django 文档中没有提到执行此操作的直接方法。
下面 link 中的答案看起来很有希望,但我不知道如何使用它。我不确定最新的 django 版本是否支持它,因为我使用 ipdb
调试它并发现 instance
变量没有属性 has_changed
如下面的答案所述.
Django: When saving, how can you check if a field has changed?
在您模型的 __init__
上进行设置,以便您可以访问它。
def __init__(self, *args, **kwargs):
super(YourModel, self).__init__(*args, **kwargs)
self.__original_mode = self.mode
现在您可以执行如下操作:
if instance.mode != instance.__original_mode:
# do something useful
通常覆盖保存方法比使用信号更好。
From Two scoops of django:
"Use signals as a last resort."
我同意@scoopseven 关于在 init 上缓存原始值的回答,但如果可能的话覆盖保存方法。
class Mode(models.Model):
name = models.CharField(max_length=5)
mode = models.BooleanField()
__original_mode = None
def __init__(self, *args, **kwargs):
super(Mode, self).__init__(*args, **kwargs)
self.__original_mode = self.mode
def save(self, force_insert=False, force_update=False, *args, **kwargs):
if self.mode != self.__original_mode:
# then do this
else:
# do that
super(Mode, self).save(force_insert, force_update, *args, **kwargs)
self.__original_mode = self.mode
如果你想比较保存操作前后的状态,你可以使用 pre_save
信号,它为你提供实例,因为它在数据库更新后应该变成这样,在 pre_save 中你可以读取当前状态数据库中的实例并根据差异执行一些操作。
from django.db.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save, sender=MyModel)
def on_change(sender, instance: MyModel, **kwargs):
if instance.id is None: # new object will be created
pass # write your code here
else:
previous = MyModel.objects.get(id=instance.id)
if previous.field_a != instance.field_a: # field will be updated
pass # write your code here
这是一个老问题,但我最近遇到过这种情况,我通过执行以下操作完成了它:
class Mode(models.Model):
def save(self, *args, **kwargs):
if self.pk:
# If self.pk is not None then it's an update.
cls = self.__class__
old = cls.objects.get(pk=self.pk)
# This will get the current model state since super().save() isn't called yet.
new = self # This gets the newly instantiated Mode object with the new values.
changed_fields = []
for field in cls._meta.get_fields():
field_name = field.name
try:
if getattr(old, field_name) != getattr(new, field_name):
changed_fields.append(field_name)
except Exception as ex: # Catch field does not exist exception
pass
kwargs['update_fields'] = changed_fields
super().save(*args, **kwargs)
这更有效,因为它捕获了来自应用程序和 django-admin 的所有 updates/saves。
在 post_save
方法中你有 kwargs
参数,它是一个字典并包含一些信息。 kwargs
中的 update_fields
告诉您更改了哪些字段。此字段存储为 forzenset
对象。您可以检查哪些字段发生了这样的变化:
@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
if not created:
for item in iter(kwargs.get('update_fields')):
if item == 'field_name' and instance.field_name == "some_value":
# do something here
但是这个解决方案有一个问题。例如,如果您的字段值为 10,并且您再次将此字段更新为 10,则此字段将再次处于 update_fields
。
我来晚了,但这对其他人有帮助。
我们可以为此定制信号。
使用自定义信号我们可以轻松地做这些事情:
- Post是否创建
- Post是否修改
- Post 已保存但任何字段未更改
class Post(models.Model):
# some fields
自定义信号
**用参数发出信号**
from django.dispatch import Signal, receiver
# provide arguments for your call back function
post_signal = Signal(providing_args=['sender','instance','change','updatedfields'])
注册带回调函数的信号
# register your signal with receiver decorator
@receiver(post_signal)
def post_signalReciever(sender,**kwargs):
print(kwargs['updatedfields'])
print(kwargs['change'])
正在从 post-admin
发送信号
我们从 Post admin 发送信号,并在对象实际修改时保存对象
#sending the signals
class PostAdmin(admin.ModelAdmin):
# filters or fields goes here
#save method
def save_model(self, request, obj, form, change):
if not change and form.has_changed(): # new post created
super(PostAdmin, self).save_model(request, obj, form, change)
post_signal.send(self.__class__,instance=obj,change=change,updatedfields=form.changed_data)
print('Post created')
elif change and form.has_changed(): # post is actually modified )
super(PostAdmin, self).save_model(request, obj, form, change)
post_signal.send(self.__class__,instance=obj,change=change,updatedfields=form.changed_data)
print('Post modified')
elif change and not form.has_changed() :
print('Post not created or not updated only saved ')
另请参阅:
您可以在 Django 信号中使用 update_fields
。
@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
# only update instance
if not created:
update_fields = kwargs.get('update_fields') or set()
# value of `mode` has changed:
if 'mode' in update_fields:
# then do this
pass
else:
# do that
pass
这可以用instance._state.adding
来识别
if not instance._state.adding:
# update to existing record
do smthng
else:
# new object insert operation
do smthng
我正在使用 django 的 post_save 信号在保存模型后执行一些语句。
class Mode(models.Model):
name = models.CharField(max_length=5)
mode = models.BooleanField()
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
# do some stuff
pass
现在我想根据mode
字段的值是否发生变化来执行一条语句。
@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
# if value of `mode` has changed:
# then do this
# else:
# do that
pass
我查看了一些 SOF 线程和博客,但找不到解决方案。他们都试图使用 pre_save 方法或表单,这不是我的用例。 https://docs.djangoproject.com/es/1.9/ref/signals/#post-save 在 django 文档中没有提到执行此操作的直接方法。
下面 link 中的答案看起来很有希望,但我不知道如何使用它。我不确定最新的 django 版本是否支持它,因为我使用 ipdb
调试它并发现 instance
变量没有属性 has_changed
如下面的答案所述.
Django: When saving, how can you check if a field has changed?
在您模型的 __init__
上进行设置,以便您可以访问它。
def __init__(self, *args, **kwargs):
super(YourModel, self).__init__(*args, **kwargs)
self.__original_mode = self.mode
现在您可以执行如下操作:
if instance.mode != instance.__original_mode:
# do something useful
通常覆盖保存方法比使用信号更好。
From Two scoops of django: "Use signals as a last resort."
我同意@scoopseven 关于在 init 上缓存原始值的回答,但如果可能的话覆盖保存方法。
class Mode(models.Model):
name = models.CharField(max_length=5)
mode = models.BooleanField()
__original_mode = None
def __init__(self, *args, **kwargs):
super(Mode, self).__init__(*args, **kwargs)
self.__original_mode = self.mode
def save(self, force_insert=False, force_update=False, *args, **kwargs):
if self.mode != self.__original_mode:
# then do this
else:
# do that
super(Mode, self).save(force_insert, force_update, *args, **kwargs)
self.__original_mode = self.mode
如果你想比较保存操作前后的状态,你可以使用 pre_save
信号,它为你提供实例,因为它在数据库更新后应该变成这样,在 pre_save 中你可以读取当前状态数据库中的实例并根据差异执行一些操作。
from django.db.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save, sender=MyModel)
def on_change(sender, instance: MyModel, **kwargs):
if instance.id is None: # new object will be created
pass # write your code here
else:
previous = MyModel.objects.get(id=instance.id)
if previous.field_a != instance.field_a: # field will be updated
pass # write your code here
这是一个老问题,但我最近遇到过这种情况,我通过执行以下操作完成了它:
class Mode(models.Model):
def save(self, *args, **kwargs):
if self.pk:
# If self.pk is not None then it's an update.
cls = self.__class__
old = cls.objects.get(pk=self.pk)
# This will get the current model state since super().save() isn't called yet.
new = self # This gets the newly instantiated Mode object with the new values.
changed_fields = []
for field in cls._meta.get_fields():
field_name = field.name
try:
if getattr(old, field_name) != getattr(new, field_name):
changed_fields.append(field_name)
except Exception as ex: # Catch field does not exist exception
pass
kwargs['update_fields'] = changed_fields
super().save(*args, **kwargs)
这更有效,因为它捕获了来自应用程序和 django-admin 的所有 updates/saves。
在 post_save
方法中你有 kwargs
参数,它是一个字典并包含一些信息。 kwargs
中的 update_fields
告诉您更改了哪些字段。此字段存储为 forzenset
对象。您可以检查哪些字段发生了这样的变化:
@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
if not created:
for item in iter(kwargs.get('update_fields')):
if item == 'field_name' and instance.field_name == "some_value":
# do something here
但是这个解决方案有一个问题。例如,如果您的字段值为 10,并且您再次将此字段更新为 10,则此字段将再次处于 update_fields
。
我来晚了,但这对其他人有帮助。
我们可以为此定制信号。
使用自定义信号我们可以轻松地做这些事情:
- Post是否创建
- Post是否修改
- Post 已保存但任何字段未更改
class Post(models.Model):
# some fields
自定义信号
**用参数发出信号**
from django.dispatch import Signal, receiver
# provide arguments for your call back function
post_signal = Signal(providing_args=['sender','instance','change','updatedfields'])
注册带回调函数的信号
# register your signal with receiver decorator
@receiver(post_signal)
def post_signalReciever(sender,**kwargs):
print(kwargs['updatedfields'])
print(kwargs['change'])
正在从 post-admin
发送信号我们从 Post admin 发送信号,并在对象实际修改时保存对象
#sending the signals
class PostAdmin(admin.ModelAdmin):
# filters or fields goes here
#save method
def save_model(self, request, obj, form, change):
if not change and form.has_changed(): # new post created
super(PostAdmin, self).save_model(request, obj, form, change)
post_signal.send(self.__class__,instance=obj,change=change,updatedfields=form.changed_data)
print('Post created')
elif change and form.has_changed(): # post is actually modified )
super(PostAdmin, self).save_model(request, obj, form, change)
post_signal.send(self.__class__,instance=obj,change=change,updatedfields=form.changed_data)
print('Post modified')
elif change and not form.has_changed() :
print('Post not created or not updated only saved ')
另请参阅:
您可以在 Django 信号中使用 update_fields
。
@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
# only update instance
if not created:
update_fields = kwargs.get('update_fields') or set()
# value of `mode` has changed:
if 'mode' in update_fields:
# then do this
pass
else:
# do that
pass
这可以用instance._state.adding
来识别if not instance._state.adding:
# update to existing record
do smthng
else:
# new object insert operation
do smthng