#35031: overridden **from_db** classmethod doesn't get called if put in the
AbstractBaseModel
-------------------------------------+-------------------------------------
Reporter: Gaurav | Owner: nobody
Jain |
Type: Bug | Status: new
Component: Database | Version: 4.2
layer (models, ORM) |
Severity: Normal | Keywords:
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
Context:
As described here,
https://docs.djangoproject.com/en/4.2/ref/models/instances/#customizing-
model-loading, I'm overriding **from_db** method to keep track of old and
new values so that I can take necessary actions if the value has changed.
This works fine when I'm overriding this method in individual classes,
however, this method (from_db) doesn't get called when I'm putting this in
the AbstractBaseModel class as described below.
Reproducing steps.
{{{
class AbstractBaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
@classmethod
def from_db(cls, db, field_names, values):
instance = super().from_db(db, field_names, values)
# save original values, when model is loaded from database
instance._initial_values = dict(zip(field_names, values,
strict=True))
return instance
}}}
To include the above DateTime fields in all models, just extend
it(AbstractBaseModel). Also, override the save method so that we can check
the value before saving the object. e.g. below
{{{
class Country(AbstractBaseModel):
name = models.CharField()
def save(self, *args, **kwargs):
if self.name != self._initial_values["name"]: # This will raise
AttributeError: 'Country' object has no attribute '_initial_values'
# `name` field value has changed/overridden. write
appropriate logic
print(f"New name: {self.name} and Old Name:
{self._initial_values["name"]}")
super().save(*args, **kwargs)
}}}
Above save method will raise the error as the parent class
(AbstractBaseModel) method hasn't been executed so the **_initial_values**
attribute won't be set. However, if you override the **from_db** method
inside the individual model classes, it would work. e.g.
{{{
class Country(AbstractBaseModel):
name = models.CharField()
def save(self, *args, **kwargs):
if self.name != self._initial_values["name"]: # This will work
# `name` field value has changed/overridden. write
appropriate logic
print(f"New name: {self.name} and Old Name:
{self._initial_values["name"]}")
super().save(*args, **kwargs)
@classmethod
def from_db(cls, db, field_names, values):
instance = super().from_db(db, field_names, values)
# save original values, when model is loaded from database
instance._initial_values = dict(zip(field_names, values,
strict=True))
return instance
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/35031>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107018c5e582f6a-0d6db358-635a-41b4-a518-db6bfe8d8945-000000%40eu-central-1.amazonses.com.