Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在django模型中跟踪自上次保存以来的更改_Python_Django_Django Models - Fatal编程技术网

Python 在django模型中跟踪自上次保存以来的更改

Python 在django模型中跟踪自上次保存以来的更改,python,django,django-models,Python,Django,Django Models,有几次我遇到这样的情况,在节省时间时,我需要知道哪些模型字段将被更新,并相应地采取行动 最明显的解决方案是获取主键字段并从数据库检索模型副本: 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.g

有几次我遇到这样的情况,在节省时间时,我需要知道哪些模型字段将被更新,并相应地采取行动

最明显的解决方案是获取主键字段并从数据库检索模型副本:

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)
这工作得非常好,但是,它会针对您正在保存的模型的每个实例访问数据库(如果您正在进行大量这样的保存,可能会非常不方便)

很明显,如果在模型实例生命周期的开始(
\uuuuu init\uuuu
)可以“记住”旧字段值,就不需要从数据库中检索模型的副本。所以我想出了一个小技巧:

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


也许有人知道一种更干净的方法吗?

我认为你的解决方案看起来是合理的

或者,您可以使用一个名为
get\u and\u copy()
(或其他)的管理器方法,将原始对象的副本挂起,使其脱离返回的对象。然后,您可以使用另一种管理器方法,
save_and_check()
,它利用了复制的原始文件

FWIW:如果您正在使用contrib/admin模板,则有一个名为
original
的上下文变量,它是原始对象的副本


更新:我更仔细地观察了管理员在做什么。在
类ModelAdmin
(位于django/contrib/admin/options.py)中,有一个名为
construct\u change\u message()的方法。它是由
formset.changed\u data
formset.changed\u objects
驱动的,因此django/forms/models.py
类BaseModelFormSet
就是操作所在。请参阅方法
保存现有对象()。另请查看方法
\u existing\u object()
。这比我前面提到的要复杂一些,因为它们处理多个对象的可能性,但它们基本上是在第一次访问时缓存查询集的结果。

这对fixture不起作用
loaddata
命令使用
models.Model.base\u save
。可能最干净的方法是为字段使用描述符,但必须弄清楚如何正确插入它们。

不,我不是在管理模板的上下文中问这个问题,但是我会检查它是如何完成的,谢谢提示。