I have a very basic question about django.db.models.
In this official django tutorial, if you search for word “choice_set“, you will see that variable “choice_set” is not declared anywhere, though magically, we can start using it in the code.
I wonder, what does django.db.models.Model do that magically created the *_set variable, and what other variables does it create?
You can get a full list of the attributes of a class, both those you defined and the ones that are defined for it, using the
dirfunction, just doYou’ll end up with something that looks a little like (though not exactly- I’m constructing it in a roundabout way):
That’s a lot of values! We can see exceptions like
DoesNotExistandMultipleObjectsReturned, along with the most important one,objects. But some of these attributes weren’t added by Django. If you dodir(object())you’ll find a list of the attributes in all objects:Mostly you can ignore the ones that start and end with two
__. Most of the others were added by Django.As for how and where it actually sets these: Django sets most of each new model’s attributes dynamically using the
models.Modelmetaclass. The first thing to know is that you can add a member or method to a class dynamically, using thesetattrfunction:That’s how it can create new attributes just based on the name of your attribute.
In the tutorial, the important line that allows it to start defining these extra attributes is:
This means that the
Pollclass inherits themodels.Modelclass (which belongs to Django). Inheritance has many useful properties- basically, thePollclass inherits some of the behavior that themodels.Modelclass has set up- but the place it defines most of these new attributes is in the Model metaclass. Metaclasses are a tricky concept, but basically they serve as a recipe for creating new classes, and by defining one, Django gets to step in right when themodels.pymetaclass is being defined, and define any new .The code for the Model metaclass can be found here (starting at line 55)- it’s a set of code that is actually step-by-step creating a class from the ground up. As complicated as it looks, you can get a lot out of it just by looking at the variable names. For instance, look at the promisingly named
add_to_classmethod:Outside of the one special case of
'contribute_to_class(not important for your interest), this is a method for adding a new attribute (such as a method or a member) to a class. The places where it is called give us hints of what it is adding:Here it is adding the
DoesNotExistexception, which is what is returned if you ask for aPollthat doesn’t exist. (See for yourself by runningPoll.objects.get(pk=1337), or directly by typing inPoll.DoesNotExist).But Django is actually even more complicated than that. The specific
_setattribute you’re asking about isn’t constructed for every model- it’s created when a field is related to another by aForeignKey(as are yourPollandChoice). The various places where it gets assigned are very complicated, but it basically all comes back to thisget_accessor_namefunction in related.pyThat’s just coming up with the name- tracing it back to figure out how it gets added to the class is no small feat. But I hope you see from this that Django has many chances to add attributes like this.