#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.

Reply via email to