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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23361057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 02:49:52  来源:igfitidea点击:

django - comparing old and new field value before saving

pythondjangodjango-signals

提问by Y.N

I have a django model, and I need to compare old and new values of field BEFORE saving.

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

I've tried the save() inheritence, and pre_save signal. It was triggered correctly, but I can't find the list of actualy changed fields and can't compare old and new values. There is a way? I need it for optimization of presave actions.

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

Thank you!

谢谢!

采纳答案by Odif Yltsaeb

There is very simple django way for doing it.

有非常简单的 django 方法来做到这一点。

"Memorise" the values in model init like this:

像这样“记住”模型初始化中的值:

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

Real life example:

现实生活中的例子:

At class:

上课时:

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

And then in modelform save method:

然后在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

回答by Sahil kalra

It is better to do this at ModelForm level.

最好在ModelForm 级别执行此操作

There you get all the Data that you need for comparison in save method:

在那里,您可以获得在 save 方法中进行比较所需的所有数据:

  1. self.data: Actual Data passed to the Form.
  2. self.cleaned_data: Data cleaned after validations, Contains Data eligible to be saved in the Model
  3. self.changed_data: List of Fields which have changed. This will be empty if nothing has changed
  1. self.data:传递给表单的实际数据。
  2. self.cleaned_data: 验证后清理的数据,包含有资格保存在模型中的数据
  3. self.changed_data:已更改的字段列表。如果没有任何变化,这将是空的

If you want to do this at Model level then you can follow the method specified in Odif's answer.

如果您想在模型级别执行此操作,则可以按照 Odif 的答案中指定的方法进行操作。

回答by psl

Also you can use FieldTrackerfrom django-model-utilsfor this:

你也可以使用FieldTrackerDjango的模型utils的这个:

  1. Just add tracker field to your model:

    tracker = FieldTracker()
    
  2. Now in pre_save and post_save you can use:

    instance.tracker.previous('modelfield')     # get the previous value
    instance.tracker.has_changed('modelfield')  # just check if it is changed
    
  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
    

回答by lehins

Here is an app that gives you access to previous and current value of a field right before model will be saved: django-smartfields

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

Here is how this problem can be solved in a nice declarative may:

以下是如何在一个漂亮的声明性可能中解决这个问题:

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())
    ])

Moreover, this processor will be invoked, only in case that field's value was replaced

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

回答by Bobby

My use case for this was that I needed to set a denormalized value in the model whenever some field changed its value. However, as the field being monitored was a m2m relation, I didn't want to have to do that DB lookup whenever save was called in order to check whether the denormalized field needed updating. So, instead I wrote this little mixin (using @Odif Yitsaeb's answer as inspiration) in order to only update the denormalized field when necessary.

我的用例是,每当某个字段更改其值时,我都需要在模型中设置一个非规范化值。然而,由于被监控的字段是一个 m2m 关系,我不想在每次调用 save 时都必须执行数据库查找以检查非规范化字段是否需要更新。所以,我写了这个小小的 mixin(使用@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

回答by erika_dike

I agree with Sahil that it is better and easier to do this with ModelForm. However, you would customize the ModelForm's clean method and perform validation there. In my case, I wanted to prevent updates to a model's instance if a field on the model is set.

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

My code looked like this:

我的代码如下所示:

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

回答by Slipstream

Something like this also works:

像这样的东西也有效:

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)