I’ve been playing around the class models and stumble upon this weird example
class CustomManager(models.Manager):
def custom_method(self):
return self.filter(some_field=True)
class BaseModelWithCustomManager:
objects = CustomManager()
class MyModel(BaseModelWithCustomManager, models.Model):
name = models.CharField(max_length=255)
some_field = models.BooleanField(default=False)
With this code, MyModel.objects
will not be an instance of the CustomManager
, rather the default instance of models.Manager
. My question is, why?
I realize this is not the recommended way of django manager customization, I am simply curious of the mechanism behind this.
I tried to read the source code of model.Models
and model.Manager
, unfortunately to no avail as i couldn’t comprehend much.
Welcome @Double-T1 !
Side note: It’s not just managers that don’t show up. You can’t define a field in that parent class, either.
There’s a lot of metaprogramming “magic” (to me) that occurs in model class initialization.
However, superficially, it appears that the key code for this appears right at the top of the __new__
method in ModelBase
:
def __new__(cls, name, bases, attrs, **kwargs):
super_new = super().__new__
# Also ensure initialization is only performed for subclasses of Model
# (excluding Model class itself).
parents = [b for b in bases if isinstance(b, ModelBase)]
My guess is that comment really says it all - during the class construction process, only subclasses of ModelBase
are going to be initialized, which means that your parent class won’t be involved with this at all.
While my personal confidence level on this is rather low, I do believe that the __new__
method is likely the area where you would want to focus on if you want a better understanding of what’s happening.
You might find it illustrative to add some print statements to that method, or even walk it through with a debugger, to see what is happening.
2 Likes
are you sure you called MyModel.objects.custom_method()
?
will give it a try, thanks!
did you try this?
class MyModel(models.Model):
objects = CustomManager()
name = models.CharField(max_length=255)
some_field = models.BooleanField(default=False)
yes, if I call MyModel.objects.custom_method()
it wouldn’t work. It would show an error saying that objects
does not have the method custom_method
.
And when i use pdb to check the type of My.Model.objects
, it would show that it’s an instance of models.Manager
instead of CustomManager
.
This is the recommended way by the documentation, so it would work.
But I’m more curious of the reason behind the malfunctioning of my example, as the case negates my understanding of python OOP.
Again, it’s because Model classes are not “typical” or “standard” Python classes. The classes are not defined using the default methods. A Model
class is dynamically created by the ModelBase
class, which means they don’t present themselves as if you were creating other classes.
I suggest you read the docs at Data model, Metaclasses and the section on __new__
on that same page. You might also find the docs at Background Theory to provide some insights. (While not 100% on-point, it does reference some of the relevent issues here.)
1 Like
I’ve figured it out. Learned a lot about python and django during the process. Thanks for everything.