python - 在 Django 模型中跟踪自上次保存以来的变化

标签 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.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 的非公共(public)接口(interface)。

也许有人知道更简洁的方法?

最佳答案

我认为您的解决方案看起来很合理。

或者,您可以使用名为 get_and_copy()(或其他名称)的管理器方法,将原始对象的副本卡在返回的内容之外。然后,您可以使用另一个管理器方法 save_and_check(),它利用了复制的原件。

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

更新:我更仔细地查看了管理员在做什么。在 class ModelAdmin(位于 django/contrib/admin/options.py)中有一个名为 construct_change_message() 的方法。它由 formset.changed_dataformset.changed_objects 驱动,因此 django/forms/models.py class BaseModelFormSet 是 Action 所在的位置。请参阅方法 save_existing_objects()。另请查看方法 _existing_object()。它比我之前提到的要复杂一点,因为它们处理的是多个对象的可能性,但它们基本上是缓存第一次访问时查询集的结果。

关于python - 在 Django 模型中跟踪自上次保存以来的变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2029814/

相关文章:

python - 根据名称进行字符串分组

django - 无法在 post_delete 中删除 FileField 的文件

django - 特定 Django 模型的高级搜索

python - 枕头 Image.convert TypeError : argument 1 must be str, 不是 int

Python Fabric 从单独的文件加载配置

ajax - django ajax 运行时错误 - URL 不以斜杠结尾

django - 在 Django 中生成 TSV 文件

python - 使用正则表达式创建包罗万象的 url() 条目

django - `basename` 未指定参数,并且可能 '\

python - Pandas Dataframe 到字典 groupby 索引