有几次我遇到了这种情况,在节省时间我需要知道哪些模型字段将被更新并采取相应的行动。
最明显的解决方案是获取主键字段并从数据库中检索模型的副本:
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
的非公共接口。
也许有人知道更清洁的方法吗?
答案 0 :(得分:2)
我认为您的解决方案看起来很合理。
或者,您可以使用名为get_and_copy()
(或其他)的Manager方法,该方法将原始对象的副本挂起。然后,您可以使用另一种管理方法save_and_check()
,该方法利用了复制的原始文件。
FWIW:如果您正在使用contrib / admin模板,则会有一个名为original
的上下文变量,它是原始对象的副本。
更新:我更密切地关注管理员正在做什么。在class ModelAdmin
(位于django / contrib / admin / options.py)中,有一个名为construct_change_message()
的方法。它由formset.changed_data
和formset.changed_objects
驱动,因此django / forms / models.py class BaseModelFormSet
是操作的所在。请参阅方法save_existing_objects()
。另请参阅方法_existing_object()
。它比我前面提到的要复杂一点,因为它们处理多个对象的可能性,但它们基本上是在第一次访问时缓存查询集的结果。
答案 1 :(得分:0)
这不适用于灯具。 loaddata
命令使用models.Model.base_save
。可能最干净的方法是对字段使用描述符,但必须弄清楚如何正确插入它们。