2010-01-08 32 views
9

有幾次我遇到了情況,在保存時我需要知道哪些模型字段將被更新並採取相應的行動。保持跟蹤上次保存在django模型中以來的變化

這個最明顯的解決方案是採取主鍵字段,並從數據庫中檢索模型的副本:

class MyModel(models.Model): 

    def save(self, force_insert=False, force_update=False, using=None): 
     if self.id is not None: 
      unsaved_copy = MyModel.objects.get(id=self.id) 
      # Do your comparisons here 
     super(MyModel, self).save(force_insert, force_update, using) 

這工作完全正常,但是,它擊中了數據庫的每個實例你正在保存的模型(如果你正在做很多這樣的保存,可能會很不方便)。

很明顯,如果在模型實例的生命週期開始時(__init__)能夠「記住」舊字段值,則不需要從數據庫中檢索模型的副本。所以,我想出了這個小劈:

class MyModel(models.Model): 

    def __init__(self, *args, **kwargs): 
     super(MyModel, self).__init__(*args, **kwargs) 
     self.unsaved = {} 
     for field in self._meta.fields: 
      self.unsaved[field.name] = getattr(self, field.name, None) 

    def save(self, force_insert=False, force_update=False, using=None): 
     for name, value in self.unsaved.iteritems(): 
      print "Field:%s Old:%s New:%s" % (name, value, getattr(self, name, None)) 
     # old values can be accessed through the self.unsaved member 
     super(MyModel, self).save(force_insert, force_update, using) 

這似乎是工作,但它利用了django.db.models.Model非公共接口。

也許有人知道一個更乾淨的方式來做到這一點?

回答

2

我認爲你的解決方案看起來很合理。

或者,您可以使用名爲get_and_copy()(或其他)的Manager方法將原始對象的副本掛起返回的內容。然後,您可以使用另一個Manager方法,save_and_check(),它利用了複製的原件。如果您使用的是contrib/admin模板,則會有一個名爲original的上下文變量,它是原始對象的副本。

更新:我更關注管理員在做什麼。在class ModelAdmin(位於django/contrib/admin/options.py)中有一個名爲construct_change_message()的方法。它由formset.changed_dataformset.changed_objects驅動,因此django/forms/models.py class BaseModelFormSet就是動作所在。請參閱方法save_existing_objects()。也請看方法_existing_object()。它比我之前提到的要複雜一點,因爲它們處理多個對象的可能性,但它們基本上緩存了第一次訪問時設置的查詢結果。

+0

不,我沒有在管理員模板的環境中提問,但是我會檢查它是如何完成的,謝謝你的提示。 – shylent 2010-01-09 08:12:48

0

這不適用於燈具。 loaddata命令使用models.Model.base_save。最簡潔的方法可能是在字段中使用描述符,但必須弄清楚如何正確插入它們。