Django中的脏字段

39 投票
10 回答
31145 浏览
提问于 2025-04-11 09:17

在我的应用程序中,我需要在模型保存时保存更改的值(旧值和新值)。有没有什么例子或者可以用的代码?

我需要这个功能是为了内容的审核。比如说,如果用户在模型中做了什么更改,那么管理员就可以在一个单独的表格里看到所有的更改,然后决定是否要应用这些更改。

10 个回答

14

我再补充一个回答,因为自从这个问题最初被提出来后,很多事情都发生了变化

现在在Django的世界里,有很多应用程序可以解决这个问题。你可以在Django Packages网站上找到一个完整的模型审计和历史记录应用程序列表

我写过一篇博客文章,比较了其中一些应用程序。这篇文章现在已经有4年了,稍微有点过时。不过,解决这个问题的不同方法似乎还是差不多的。

这些方法有:

  1. 把所有历史变化以序列化的格式(比如JSON)存储在一个表里
  2. 为每个模型在一个与原始表相似的表中存储所有历史变化
  3. 把所有历史变化存储在与原始模型同一个表里(我不推荐这种方法)

django-reversion这个包似乎仍然是解决这个问题最受欢迎的方案。它采用了第一种方法:序列化变化,而不是复制表。

几年前我重新维护了django-simple-history。它采用了第二种方法:复制每个表。

所以我建议使用一个应用程序来解决这个问题。现在有几个流行的应用程序效果都不错。

哦,如果你只是想检查字段的变化,而不需要存储所有历史变化,可以看看django-model-utils中的FieldTracker

27

我觉得Armin的想法非常有用。这是我自己的改进版本;

class DirtyFieldsMixin(object):
    def __init__(self, *args, **kwargs):
        super(DirtyFieldsMixin, self).__init__(*args, **kwargs)
        self._original_state = self._as_dict()

    def _as_dict(self):
        return dict([(f.name, getattr(self, f.name)) for f in self._meta.local_fields if not f.rel])

    def get_dirty_fields(self):
        new_state = self._as_dict()
        return dict([(key, value) for key, value in self._original_state.iteritems() if value != new_state[key]])

补充一下,我已经测试过这个了。

抱歉,代码行有点长。主要的不同是(除了名字)它只缓存本地的非关系字段。换句话说,如果有父模型的字段,它不会缓存这些字段。

还有一件事;在保存之后,你需要重置_original_state字典。但是我不想覆盖save()方法,因为大多数时候我们在保存后会丢弃模型实例。

def save(self, *args, **kwargs):
    super(Klass, self).save(*args, **kwargs)
    self._original_state = self._as_dict()
14

你没有详细说明你的具体需求和使用场景。特别是,了解你需要如何处理这些变更信息会很有帮助(你需要保存多久?)。如果你只是想临时保存这些信息,@S.Lott 提出的会话解决方案可能是最好的选择。如果你想要一个完整的审计记录,记录数据库中所有对象的变化,可以试试这个 审计记录解决方案

更新:我上面提到的审计记录代码是我见过的最接近你需求的完整解决方案,虽然它有一些限制(对于多对多字段完全不适用)。它会在数据库中保存你对象的所有历史版本,这样管理员就可以回滚到任何一个之前的版本。如果你希望变更在批准之前不生效,你需要对它进行一些调整。

你也可以基于 @Armin Ronacher 的 DiffingMixin 自定义一个解决方案。你可以把差异字典(可能需要序列化)存储在一个表中,供管理员以后查看并根据需要应用(你需要编写代码,将差异字典应用到某个实例上)。

撰写回答