2010-01-08 55 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。最简洁的方法可能是在字段中使用描述符,但必须弄清楚如何正确插入它们。