programing

django-저장하기 전에 이전 필드 값과 새 필드 값 비교

procenter 2021. 1. 17. 12:10
반응형

django-저장하기 전에 이전 필드 값과 새 필드 값 비교


장고 모델이 있고 저장하기 전에 필드의 이전 값과 새 값을 비교해야합니다.

save () 상속과 pre_save 신호를 시도했습니다. 올바르게 트리거되었지만 실제로 변경된 필드 목록을 찾을 수없고 이전 값과 새 값을 비교할 수 없습니다. 방법이 있습니까? 사전 저장 작업의 최적화를 위해 필요합니다.

감사합니다!


아주 간단한 장고 방식이 있습니다.

다음과 같이 모델 초기화의 값을 "기억"합니다.

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

ModelForm 수준 에서이 작업을 수행하는 것이 좋습니다 .

저장 방법에서 비교에 필요한 모든 데이터를 얻을 수 있습니다.

  1. self.data : Form에 전달 된 실제 데이터.
  2. self.cleaned_data : 검증 후 정리 된 데이터, 모델에 저장할 수있는 데이터 포함
  3. self.changed_data : 변경된 필드 목록. 아무것도 변경되지 않으면 비어 있습니다.

모델 수준에서이 작업을 수행하려면 Odif의 답변에 지정된 방법을 따를 수 있습니다.


또한 django-model-utils 에서 FieldTracker사용할 수 있습니다 .

  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
    

다음은 모델이 저장되기 직전에 필드의 이전 값과 현재 값에 액세스 할 수있는 앱입니다. 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())
    ])

또한이 프로세서는 해당 필드의 값이 대체 된 경우에만 호출됩니다.


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.

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

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.

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

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)

ReferenceURL : https://stackoverflow.com/questions/23361057/django-comparing-old-and-new-field-value-before-saving

반응형