django - 在保存之前比较新旧字段值

问题描述 投票:48回答:7

我有一个django模型,我需要在保存之前比较字段的新旧值。

我已经尝试了save()继承和pre_save信号。它被正确触发,但我找不到实际更改字段的列表,无法比较新旧值。有一种方法?我需要它来优化预先行动。

谢谢!

python django django-signals
7个回答
49
投票

这样做有很简单的django方式。

“记住”模型初始化中的值,如下所示:

def __init__(self, *args, **kwargs):
    super(MyClass, self).__init__(*args, **kwargs)
    self.initial_parametername = self.parametername
    ---
    self.initial_parameternameX = self.parameternameX

真人生活的例子:

上课时:

def __init__(self, *args, **kwargs):
    super(MyClass, self).__init__(*args, **kwargs)
    self.__important_fields = ['target_type', 'target_id', 'target_object', 'number', 'chain', 'expiration_date']
    for field in self.__important_fields:
        setattr(self, '__original_%s' % field, getattr(self, field))

def has_changed(self):
    for field in self.__important_fields:
        orig = '__original_%s' % field
        if getattr(self, orig) != getattr(self, field):
            return True
    return False

然后在modelform保存方法:

def save(self, force_insert=False, force_update=False, commit=True):
    # Prep the data
    obj = super(MyClassForm, self).save(commit=False)

    if obj.has_changed():

        # If we're down with commitment, save this shit
        if commit:
            obj.save(force_insert=True)

    return obj

33
投票

最好在ModelForm级别执行此操作。

在保存方法中,您可以获得比较所需的所有数据:

  1. self.data:传递给表单的实际数据。
  2. self.cleaned_data:验证后清理数据,包含有资格保存在模型中的数据
  3. self.changed_data:已更改的字段列表。如果没有任何改变,这将是空的

如果你想在Model级别执行此操作,那么你可以按照Odif的答案中指定的方法进行操作。


25
投票

你也可以使用FieldTrackerdjango-model-utils

  1. 只需在您的模型中添加跟踪器字段: tracker = FieldTracker()
  2. 现在在pre_save和post_save中你可以使用: instance.tracker.previous('modelfield') # get the previous value instance.tracker.has_changed('modelfield') # just check if it is changed

1
投票

这是一个应用程序,可让您在保存模型之前访问字段的上一个和当前值:django-smartfields

以下是一个很好的声明可以解决这个问题的方法:

from django.db import models
from smartfields import fields, processors
from smartfields.dependencies import Dependency

class ConditionalProcessor(processors.BaseProcessor):

    def process(self, value, stashed_value=None, **kwargs):
        if value != stashed_value:
            # do any necessary modifications to new value
            value = ... 
        return value

class MyModel(models.Model):
    my_field = fields.CharField(max_length=10, dependencies=[
        Dependency(processor=ConditionalProcessor())
    ])

此外,仅在字段值被替换的情况下才会调用此处理器


1
投票

我的用例是每当某个字段改变其值时,我需要在模型中设置非规范化值。但是,由于被监视的字段是m2m关系,我不想在调用save时进行数据库查找,以检查非规范化字段是否需要更新。所以,我写了这个小混音(使用@Odif Yitsaeb的答案作为灵感),以便只在必要时更新非规范化的字段。

class HasChangedMixin(object):
    """ this mixin gives subclasses the ability to set fields for which they want to monitor if the field value changes """
    monitor_fields = []

    def __init__(self, *args, **kwargs):
        super(HasChangedMixin, self).__init__(*args, **kwargs)
        self.field_trackers = {}

    def __setattr__(self, key, value):
        super(HasChangedMixin, self).__setattr__(key, value)
        if key in self.monitor_fields and key not in self.field_trackers:
            self.field_trackers[key] = value

    def changed_fields(self):
        """
        :return: `list` of `str` the names of all monitor_fields which have changed
        """
        changed_fields = []
        for field, initial_field_val in self.field_trackers.items():
            if getattr(self, field) != initial_field_val:
                changed_fields.append(field)

        return changed_fields

1
投票

我同意Sahil的说法,使用ModelForm更好更容易。但是,您将自定义ModelForm的clean方法并在那里执行验证。就我而言,如果设置了模型上的字段,我想阻止对模型实例的更新。

我的代码看起来像这样:

from django.forms import ModelForm

class ExampleForm(ModelForm):
    def clean(self):
        cleaned_data = super(ExampleForm, self).clean()
        if self.instance.field:
            raise Exception
        return cleaned_data

0
投票

这样的东西也有效:

class MyModel(models.Model):
    my_field = fields.IntegerField()

    def save(self, *args, **kwargs):
       # Compare old vs new
       if self.pk:
           obj = MyModel.objects.values('my_value').get(pk=self.pk)
           if obj['my_value'] != self.my_value:
               # Do stuff...
               pass
       super().save(*args, **kwargs)
© www.soinside.com 2019 - 2024. All rights reserved.