usar update_fields trigger signal example español como python django django-signals

python - update_fields - django-comparar el valor del campo viejo y nuevo antes de guardar



trigger django (7)

Aquí hay una aplicación que le da acceso al valor anterior y actual de un campo justo antes de que se guarde el modelo: django-smartfields

Aquí es cómo se puede resolver este problema en un buen declarativo:

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

Además, este procesador será invocado, solo en caso de que el valor de ese campo sea reemplazado

Tengo un modelo django, y necesito comparar los valores antiguos y nuevos del campo ANTES de guardar.

Probé la herencia save () y la señal pre_save. Se activó correctamente, pero no puedo encontrar la lista de campos modificados actualmente y no puedo comparar valores antiguos y nuevos. ¿Hay una manera? Lo necesito para la optimización de acciones de guarda.

¡Gracias!


Es mejor hacer esto en el nivel de ModelForm .

Allí obtienes todos los datos que necesitas para comparar en el método save:

  1. self.data : datos reales pasados ​​al formulario.
  2. self.cleaned_data : datos limpiados después de validaciones, contiene datos elegibles para guardarse en el modelo
  3. self.changed_data : Lista de campos que han cambiado. Esto estará vacío si nada ha cambiado

Si desea hacer esto en el nivel Modelo, puede seguir el método especificado en la respuesta de Odif.


Estoy de acuerdo con Sahil en que es mejor y más fácil hacer esto con ModelForm. Sin embargo, personalizaría el método de limpieza de ModelForm y realizaría la validación allí. En mi caso, quería evitar actualizaciones a la instancia de un modelo si se configura un campo en el modelo.

Mi código se veía así:

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


Hay una forma muy simple de django para hacerlo.

"Memorice" los valores en el modelo init de esta manera:

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

Ejemplo de vida real:

En clase:

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

Y luego en el método de guardar modelo:

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


Mi caso de uso para esto era que necesitaba establecer un valor desnormalizado en el modelo cada vez que un campo cambiaba su valor. Sin embargo, como el campo que se monitoreaba era una relación m2m, no quería tener que hacer esa búsqueda DB cada vez que se llamaba a guardar para verificar si el campo desnormalizado necesitaba actualización. Entonces, en cambio, escribí este pequeño mixin (usando la respuesta de @Odif Yitsaeb como inspiración) para actualizar solo el campo desnormalizado cuando sea necesario.

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


También puede usar FieldTracker de django-model-utils para esto:

  1. Simplemente agregue el campo de seguimiento a su modelo:

    tracker = FieldTracker()

  2. Ahora en pre_save y post_save puedes usar:

    instance.tracker.previous(''modelfield'') # get the previous value instance.tracker.has_changed(''modelfield'') # just check if it is changed


Algo como esto también funciona:

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)