Django管理界面:检测对象字段的子集是否已更改以及它们中的哪些字段已更改

7

我需要在管理员中检测某些模型的字段何时发生了更改,以便根据更改的字段和这些字段的先前/当前值稍后发送通知。

我尝试使用ModelForm并重写save()方法,但表单的self.cleaned_dataseld.instance已经具有字段的新值。

4个回答

7
修改上述答案...使用来自Dominik Szopa的出色函数并进行更改将解决您的关系更改检测问题:使用下面的代码:
def get_changes_between_models(model1, model2, excludes = []):
    changes = {}
    for field in model1._meta.fields:
        if not (field.name in excludes):
            if field.value_from_object(model1) != field.value_from_object(model2):
                changes[field.verbose_name] = (field.value_from_object(model1),
                                                   field.value_from_object(model2))
    return changes

在你的代码中,你可以这样写(出于性能原因,请避免使用try/except):

if (self.id):
    old = MyModel.Objects.get(pk=self.id)
    changes = get_changes_between_models(self, old)

    if (changes):
        # Process based on what is changed.

如果您是在“模型”层面上进行此操作,那么没有办法保存额外的查询。在到达“保存”点之前,数据已经发生了改变。这是我的第一篇帖子,如果我听起来像个白痴,请原谅我。

谢谢你关于Field.value_from_object()的提示!显然它提供了一种一致比较值的方式。不错! - Armando Pérez Marqués
为了使这个答案更易读、精确和清晰,我会将"model1"改为"model_instance_1","model2"改为"model_instance_2"。而且方法名可能应该是"get_changes_between_model_instances"。不确定在更近版本的Django中是否有更好的方法,但我目前正在使用1.8,这个答案对我来说可行。 - jenniwren

7
为了避免额外的数据库查询,我修改了构造函数以记住初始值,并在稍后的保存方法中使用它:
class Package(models.Model):
    feedback = models.IntegerField(default = 0, choices = FEEDBACK_CHOICES)
    feedback_time = models.DateTimeField(null = True)

    def __init__(self, *args, **kw):
        super(Package, self).__init__(*args, **kw)
        self._old_feedback = self.feedback

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        if not force_insert and self.feedback != self._old_feedback:
            self.feedback_time = datetime.utcnow()
        return super(Package, self).save(force_insert, force_update, *args, **kwargs)

2
在许多情况下,我认为这似乎不能满足您的需求。例如,在表单提交时,会从POST参数初始化一个对象,然后保存该对象。保存方法将无法找到初始值和当前字段值之间的差异;但它们与数据库中的值是不同的。 - Greg Ball

1
为了获取两个模型实例的差异,您也可以使用此function。它会比较模型实例并返回更改的字典。

很有用!但似乎它不知道相关领域。对于我的目的来说,添加或删除关系(一对多或多对多)也是对象的更改。 - Armando Pérez Marqués

0
你需要在保存方法中从数据库获取正在处理的对象的额外副本,然后再完全保存它。示例:
class MyModel(models.Model):
    field1 = models.CharField(max_length=50)

    def save(self):
        if self.id:
            try:
                old = MyModel.objects.get(pk=self.id)
                if old.field1 != self.field1:
                    # Process somehow
            except MyModel.DoesNotExist:
                pass
        super(MyModel, self).save()

我也想到了类似的方法(在保存对象之前从数据库中检索它并将其与即将保存的副本进行比较),但我希望避免额外的查询。 - Armando Pérez Marqués

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接