在Django模型(.save()
)上執行更新/創建時,我希望能夠「插入」並比較一些特定的屬性,以他們以前設置(如果他們以前存在的話)。Django:在更新模型之前,我想「查看」其以前的屬性
我在想前保存信號,與原始模型做一個.objects.get(instance.id)
,但這感覺很浪費。另外,驗證已經發生在pre_save()
?
在Django模型(.save()
)上執行更新/創建時,我希望能夠「插入」並比較一些特定的屬性,以他們以前設置(如果他們以前存在的話)。Django:在更新模型之前,我想「查看」其以前的屬性
我在想前保存信號,與原始模型做一個.objects.get(instance.id)
,但這感覺很浪費。另外,驗證已經發生在pre_save()
?
注意full_clean()不會自動當你調用模型的save()方法
然後,對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',你不能確定修改是否被保存。
在這種情況下,我想你可以緩存感興趣的變量,你在初始化實例,並捕捉後保存或預存的信號。
* 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>)
和你有你需要的東西(我認爲)!!!
保存前,您可以詢問數據庫中當前存儲的值。我這樣做只記錄更改的值,但每次保存時都會導致另一個數據庫查詢。
雖然我非常贊同Sébastien Piquemal's answer我最終最終同時使用了pre_save
和post_save
信號。我不是重寫__init__()
,而是在pre_save
中做了非常類似的事情,然後檢查/比較post_save
中的值,並在滿足某些條件時從那裏發出自定義信號。
我想我仍然會接受他的答案,花在它上面的時間。我沒有看到我們在做什麼與衆不同,除了我正在做一個信號的工作,而他正在做初始化工作。
這裏是我的想法:玩弄性質。
說你有這個類:
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' 字段。
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)
您可以使用pre_save信號和比較的數據庫記錄(老版):
現在,你的「pre_save」或「post_save」信號處理程序上什麼也 變化進行覈對實例記錄(已更新,但未保存在數據庫版本中)。
採取示範像這樣的爲例:
class Person(models.Model):
Name = models.CharField(max_length=200)
在pre_save功能,您可以用分貝版本比較實例的版本 。
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)
感謝您的回答。具體而言,還是非常普遍的,我想知道某個特定領域(例如選擇領域)何時發生了變化。更進一步,我想按照你所說的做 - 在發生這種情況時發出定製信號。因此,回到我的問題 - 更新模型時,如何比較以前的值來發送此信號? (如果在post_save之前不保證該對象被保存,發出foo_has_updated信號似乎不正確,但未驗證......但這完全是另一個問題)。 – 2010-07-09 20:56:02