﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
35031	overridden **from_db** classmethod doesn't get called if put in the AbstractBaseModel	Gaurav Jain	nobody	"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
}}}"	Bug	closed	Database layer (models, ORM)	4.2	Normal	invalid			Unreviewed	0	0	0	0	0	0
