8👍
about model validation :
Note that full_clean() will not be called automatically when you call your model’s save() method
Then, about the pre-save signal
, note that you get the instance that is being saved sent as a parameter with the message. As the former version of your model exists only in the database, I don’t see where else you could get the previous value of the attributes …
You don’t tell why you want to do this so it’s hard to say, but other solutions I’m thinking of right now :
* defining a custom signal that is sent everytime the attributes you are interested in are modified... This signal would then send two arguments : new value, old value
* perform the check directly when setting the attributes
If you give more details, it might be easier…
EDIT :
That’s right … If you emit a custom ‘foo_has_updated’, you will not be sure that the modification is saved.
In this case, I guess you could cache the variables that interest you while initializing the instance, and catch the post-save OR pre-save signal.
* With pre-save, you would be able to pre-process the data, but the saving operation might fail
* With post-save, you would be sure that the data has been saved.
Caching your variables could be done like this :
class CachedModel(models.Model):
cached_vars = [var1, var2, varN]
def __init__(self, *args, **kwargs):
super(CachedModel, self).__init__(*args, **kwargs)
self.var_cache = {}
for var in self.cached_vars:
self.var_cache[var] = copy.copy(getattr(self, var))
Or something like this … Then, in your signal handler :
def post_save_handler(sender, **kwargs):
instance = kwargs["instance"]
[(instance.var_cache[var], getattr(instance, var)) for var in instance.cached_var]
#[(<initial value>, <saved value>)
And you got what you needed (I think)!!!
2👍
Here’s my idea: play around with properties.
Say you have this class:
class Foo(models.Model):
name = models.CharField()
Instead, rename your field (you won’t need a migration if it’s the first time
you’re doing this) and:
class Foo(models.Model):
_name = models.CharField()
@property
def name(self):
return self._name
@name.setter
def name(self, new_value):
if not getattr(self, '_initial_name', False):
self._initial_name = self._name
if new_value != self._initial_name:
self._name_changed = True
else:
self._name_changed = False
self._name = new_value
We added two attributes to your Foo instances: ‘_initial_name’ and
‘_name_changed’ and a property: ‘name’. These are not model fields and will
never be saved to the database. Also, you won’t have to mess with the ‘_name’
field any longer as long as the ‘name’ property takes care of everything.
Now, your ‘pre_save’ or ‘post_save’ signal handler can make checks on what has
changed:
def handle_pre_save(sender, **kwargs):
foo = kwargs['instance']
if getattr(foo, '_name_changed', False):
log.debug("foo changed its name from '%s' to '%s'",
foo._initial_name, foo.name)
0👍
You can ask for the currently stored values in the database before you save. I have done this to log only changed values, but it causes another database query each time you save.
- Django.core.exceptions.ImproperlyConfigured: Set the SECRET_KEY environment variable
- Django Multi-Table Inheritance VS Specifying Explicit OneToOne Relationship in Models
0👍
While I very much approve of Sébastien Piquemal’s answer I ultimately ended up using both the pre_save
and post_save
signals. Instead of overriding __init__()
, I do something very similar in pre_save
, and then check/compare the values in post_save
and emit a custom signal from there if certain conditions are met.
I think I’ll still accept his answer, for the time spent on it. I don’t see where we’re doing much different, except I’m doing my work in a signal and he’s doing it during initialization.
- How can I easily mark records as deleted in Django models instead of actually deleting them?
- Postgres suddenly raise error '/usr/lib/libpq.5.dylib' (no such file)
- Django template extends not working
- How to make GET CORS request with authorization header
- Heroku Django DEBUG Setting not applied
0👍
You can use the pre_save signal and compare the db record (the old version) with the instance record (the updated, but not saved in the db version).
Take a model like this one as example:
class Person(models.Model):
Name = models.CharField(max_length=200)
In a pre_save function you can compare the instance version
with the db version.
def check_person_before_saving(sender, **kwargs):
person_instance = kwargs['instance']
if person_instance.id:
# you are saving a Person that is already on the db
# now you can get the db old version of Person before the updating
# you should wrap this code on a try/except (just in case)
person_db = Person.objects.get(id=person_instance.id)
# do your compares between person_db and person_instance
...
# connect the signal to the function. You can use a decorator if you prefer
pre_save.connect(check_person_before_saving, sender=Person)
- Django 1.7 removing Add button from inline form
- How do I pass parameters via url in django?
- Succinct way of updating a single field of a django model object
- How do you update a Django Form Meta class fields dynamically from the form constructor?
- Resize image on save