web-dev-qa-db-ja.com

django-保存する前に古いフィールド値と新しいフィールド値を比較する

Djangoモデルがあり、保存前にフィールドの古い値と新しい値を比較する必要があります。

Save()の継承とpre_saveシグナルを試しました。正しくトリガーされましたが、実際に変更されたフィールドのリストが見つからず、古い値と新しい値を比較できません。やり方がある?事前保存アクションの最適化に必要です。

ありがとうございました!

52
Y.N

非常に簡単なDjangoの方法があります。

次のようにモデルinitの値を「記憶」します。

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 saveメソッドで:

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
54
Odif Yltsaeb

ModelFormレベルでこれを行うことをお勧めします。

そこで、saveメソッドでの比較に必要なすべてのデータを取得します。

  1. self.data:フォームに渡される実際のデータ。
  2. self.cleaned_data:検証後にクリーニングされたデータ、モデルに保存できるデータが含まれています
  3. self.changed_data:変更されたフィールドのリスト。何も変更されていない場合、これは空になります

モデルレベルでこれを行う場合は、Odifの回答で指定されている方法に従うことができます。

37
Sahil kalra

また、 FieldTracker from Django-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
    
29
psl

ModelFormでこれを行う方がより簡単で簡単だというSahilの意見に同意します。ただし、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
2
erika_dike

モデルを保存する直前に、フィールドの以前および現在の値にアクセスできるアプリを次に示します。 Django-smartfields

Nice宣言型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())
    ])

さらに、このプロセッサは、フィールドの値が置き換えられた場合にのみ呼び出されます

2
lehins

これの私の使用例は、フィールドが値を変更するたびにモデルに非正規化値を設定する必要があったことです。ただし、監視対象のフィールドはm2mリレーションであるため、非正規化フィールドの更新が必要かどうかを確認するためにsaveが呼び出されるたびにそのDBルックアップを行う必要はありませんでした。そこで、必要に応じて非正規化フィールドのみを更新するために、代わりにこの小さな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
2
Bobby

このようなものも動作します:

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)
1
Slipstream