Django: overriding save() just on new objects
Today I was tasked to create a new feature: create a form that would allow the user create a record on the database. Easy peasy for Django. One requirement, though, was to have a field that would be calculated before saving just on a new record, not when updating it.
Approach 1 - Django Signals
Searching online I saw people suggesting using Django Signals to do that. I don't like this approach since, mainly, it was meant to enable doing things on other places when acting on an object. Not on the object you're dealing with.
Approach 2 - Overriding save()
Django Model save()
method can be overridden easily but, to check whether it is a new record or not, can be tricky:
You could check if your record already has a primary key associated with it. If it does, then you would be updating instead of creating:
# Do you custom stuff here and add to you instance
# ex. self.name = str.upper(name)
One thing though: you cannot do that if your model inherits from and abstract class that manually associates a custom PK like an UUID:
=
=
=
= True
=
...
By default, self.pk
is populated after saving to the database in the case of a new object with the DB auto-generated ID. That's why it works like in the example and not when using a custom PK (when inheriting from a base class, the PK is generated on instantiation because of the default=uuid.uuid64
argument).
So, the approach I'm using for now is a bit risky: using and internal field. Django Model's __init__()
method sets a _state.adding
field to True when the object is new (not saved yet):
# Do you custom stuff here and add to you instance
# ex. self.name = str.upper(name)
And that did the trick.
Approach 3 - Overriding save() but validate with a new query
For the UUID
base class (my scenario) I opted for the previous solution to avoid issuing a new query to check if the object already exists in the database before modifying it. I could have used the solution bellow:
= # self.pk already
# populated even if the
# object is new
# Do you custom stuff here and add to you instance
# ex. self.name = str.upper(name)
And that's a wrap! If you have another solution I would like to see your approach.
Cheers!