【发布时间】:2011-03-14 01:53:45
【问题描述】:
在 Django 模型 (.save()) 上执行更新/创建时,我希望能够“介入”并将某些特定属性与之前设置的属性进行比较(如果它们之前存在的话) .
我正在考虑预先保存信号,并查找原始模型执行.objects.get(instance.id),但这感觉很浪费。另外,验证是否已经在pre_save() 中进行?
【问题讨论】:
标签: django django-models django-signals
在 Django 模型 (.save()) 上执行更新/创建时,我希望能够“介入”并将某些特定属性与之前设置的属性进行比较(如果它们之前存在的话) .
我正在考虑预先保存信号,并查找原始模型执行.objects.get(instance.id),但这感觉很浪费。另外,验证是否已经在pre_save() 中进行?
【问题讨论】:
标签: django django-models django-signals
您可以使用 pre_save 信号并将 db 记录(旧版本)与实例记录(更新,但未保存在 db 版本中)进行比较。
以这样的模型为例:
class Person(models.Model):
Name = models.CharField(max_length=200)
在 pre_save 函数中,您可以比较实例版本 使用 db 版本。
def check_person_before_saving(sender, **kwargs):
person_instance = kwargs['instance']
if person_instance.id:
# you are saving a Person that is already on the db
# now you can get the db old version of Person before the updating
# you should wrap this code on a try/except (just in case)
person_db = Person.objects.get(id=person_instance.id)
# do your compares between person_db and person_instance
...
# connect the signal to the function. You can use a decorator if you prefer
pre_save.connect(check_person_before_saving, sender=Person)
【讨论】:
这是我的想法:玩转属性。
假设你有这个课程:
class Foo(models.Model):
name = models.CharField()
相反,重命名您的字段(如果是第一次,则不需要迁移 你正在这样做)并且:
class Foo(models.Model):
_name = models.CharField()
@property
def name(self):
return self._name
@name.setter
def name(self, new_value):
if not getattr(self, '_initial_name', False):
self._initial_name = self._name
if new_value != self._initial_name:
self._name_changed = True
else:
self._name_changed = False
self._name = new_value
我们为您的 Foo 实例添加了两个属性:“_initial_name”和 '_name_changed' 和一个属性:'name'。这些不是模型字段,将 永远不会被保存到数据库中。此外,您不必弄乱“_name” 只要 'name' 属性处理所有事情,字段就不再存在。
现在,您的 'pre_save' 或 'post_save' 信号处理程序可以检查有什么 改变:
def handle_pre_save(sender, **kwargs):
foo = kwargs['instance']
if getattr(foo, '_name_changed', False):
log.debug("foo changed its name from '%s' to '%s'",
foo._initial_name, foo.name)
【讨论】:
虽然我非常赞同Sébastien Piquemal's answer,但我最终还是同时使用了pre_save 和post_save 信号。我没有覆盖__init__(),而是在pre_save 中做一些非常相似的事情,然后检查/比较post_save 中的值,如果满足某些条件则从那里发出自定义信号。
我想我仍然会接受他的回答,因为我花了这么多时间。我看不出我们在哪里做的有很大不同,除了我在信号中做我的工作,而他在初始化期间做这件事。
【讨论】:
您可以在保存之前询问数据库中当前存储的值。我这样做是为了只记录更改的值,但每次保存时都会导致另一个数据库查询。
【讨论】:
请注意,当您调用模型的 save() 方法时,full_clean() 不会被自动调用
然后,关于pre-save signal,请注意您将保存的实例作为参数与消息一起发送。由于您的模型的旧版本仅存在于数据库中,我看不出您还能从哪里获得属性的先前值...
你不告诉你为什么要这样做,所以很难说,但我现在正在考虑其他解决方案:
* defining a custom signal that is sent everytime the attributes you are interested in are modified... This signal would then send two arguments : new value, old value
* perform the check directly when setting the attributes
如果您提供更多详细信息,可能会更容易......
编辑:
没错...如果您发出自定义的“foo_has_updated”,您将无法确定修改是否已保存。
在这种情况下,我想您可以在初始化实例时缓存您感兴趣的变量,并捕获 post-save OR pre-save 信号。
* With pre-save, you would be able to pre-process the data, but the saving operation might fail
* With post-save, you would be sure that the data has been saved.
可以这样缓存变量:
class CachedModel(models.Model):
cached_vars = [var1, var2, varN]
def __init__(self, *args, **kwargs):
super(CachedModel, self).__init__(*args, **kwargs)
self.var_cache = {}
for var in self.cached_vars:
self.var_cache[var] = copy.copy(getattr(self, var))
或者类似的东西......然后,在你的信号处理程序中:
def post_save_handler(sender, **kwargs):
instance = kwargs["instance"]
[(instance.var_cache[var], getattr(instance, var)) for var in instance.cached_var]
#[(<initial value>, <saved value>)
你得到了你需要的东西(我认为)!!!
【讨论】: