――I looked up what my colleague had announced and looked it up for myself and made a note of it. ――Why does a field that is a class variable disappear from a class in Django's model? There was a story ――Leave a brief (miscellaneous) story about why this happens.
--First, let's talk about class variables --Class variables are shared by all instances.
class Person:
name = 'takanory'
Person.name # => takanory
p1 = Person()
p1.name # => takanory
assert Person.name is p1.name # same object
--Next, look at Django's model definition -Although it inherits ** models.Model **, it is a normal Python class definition. --But the class variable of the field does not exist
class Person(models.Model):
name = models.CharField(max_length=255, default='takanory')
age = 37
#Ordinary class variables exist
Person.age # => 37
#Field object's class variable "name" is missing
Person.name
# => AttributeError: type object 'Person' has no attribute 'name'
#Actually, "name" is "_meta.Exists in "fields"
Person._meta.fields
# => (<django.db.models.fields.AutoField: id>, <django.db.models.fields.CharField: name>)
—— No wonder for those who normally use Django -** But if you think about it, isn't this strange behavior of Python? Question **
――To answer this question, we need to talk about metaclasses. -You can create a class object with ** type ** --Class definition is synonymous with creating a class object with ** type ** --The class is an instance of ** type **
#Ordinary class definition
class Person:
name = 'takanory'
#Create the same class object "Person" as the above class definition
Person = type('Person', tuple(), {'name': 'takanory'})
#The class object is an instance of type
assert isinstance(Person, type)
--The original class for creating a class is called ** metaclass ** --By default, ** type ** is set as ** metaclass ** to generate a class. -You can use another ** metaclass ** by specifying ** metaclass **. --In other words, you can customize the class definition method itself.
class Taka22(type):
"""
name is'takanory'Only in the case of
nickname = 'taka22'A metaclass that adds a class variable called
"""
def __new__(cls, name, bases, attrs):
if attrs.get('name') == 'takanory':
attrs['nickname'] = 'taka22'
return super().__new__(cls, name, bases, attrs)
class Person(metaclass=Taka22):
name = 'takanory'
Person.nickname # => 'taka22'
――At this point, you can imagine that the metaclass is doing the "field disappears" from the class. --Actually check the relevant part.
-** Model ** class uses a metaclass called ** ModelBase **
class Model(six.with_metaclass(ModelBase)):
_deferred = False
def __init__(self, *args, **kwargs):
# refs https://github.com/django/django/blob/master/django/db/models/base.py#L355
--six is a library for making Python2 and Python3 compatible. --There is a difference in how to specify ** metaclass ** between Python2 and Python3, so it will be filled in.
# python3
class Hoge(metaclass=NewMeta):
# python2
class Hoge(object):
__metaclass__ = NewMeta
# python3 and python2
class Hoge(six.with_metaclass(NewMeta):
--In the metaclass ** ModelBase , you haven't passed attrs (which contains class variables) to the new (super_new) of the parent class ( type **). -Only ** __module __ ** is passed as attrs. At this point, all class variables such as field objects have disappeared from the class definition.
class ModelBase(type):
"""
Metaclass for all models.
"""
def __new__(cls, name, bases, attrs):
#Parent class(type)of__new__Method
super_new = super(ModelBase, cls).__new__
# ~abridgement~
# Create the class.
module = attrs.pop('__module__')
#pass only module as attr and new_Generating class
#At this point the class variables are gone
new_class = super_new(cls, name, bases, {'__module__': module})
# refs https://github.com/django/django/blob/master/django/db/models/base.py#L67
--After this, the fields are set to ** _meta.fields ** in the model with their own ** contribute_to_class ** --Expect all field objects to have ** contribute_to_class ** --In other words, whether or not to set it in ** _meta.fields ** is transferred to ** Field ** instead of ** Model **. -Class variables that do not have ** contribute_to_class ** will be set as is
def add_to_class(cls, name, value):
# We should call the contribute_to_class method only if it's bound
if not inspect.isclass(value) and hasattr(value, 'contribute_to_class'):
value.contribute_to_class(cls, name) #value is a field object
else:
# contribute_to_I don't have a class
#attr is set as it is in the class definition(Be returned)
setattr(cls, name, value)
# refs https://github.com/django/django/blob/c339a5a6f72690cd90d5a653dc108fbb60274a20/django/db/models/base.py#L303
-Set to ** Model._meta.fields ** in ** contribute_to_class ** of ** Field **
def contribute_to_class(self, cls, name, private_only=False, virtual_only=NOT_PROVIDED):
# ~abridgement
#About this area, I received cls(=model)of_I'm adding myself as fields to meta.
self.model = cls
if private_only:
cls._meta.add_field(self, private=True)
else:
cls._meta.add_field(self)
if self.choices:
setattr(cls, 'get_%s_display' % self.name,
curry(cls._get_FIELD_display, field=self))
# refs https://github.com/django/django/blob/master/django/db/models/fields/__init__.py#L678
--How do class variables such as fields disappear from Django's model? -** That's because Django's Model deliberately removes it from class variables in its own metaclass (ModelBase) ** ――It's a complete guess from here, but I wondered if the intention of this implementation was to protect the field object when instantiating the model.
class Person(models.Model):
name = models.CharField(max_length=255, default='takanory')
p1 = Person()
p1.name # => 'takanory'
#The instance can directly refer to the value string "takanory"
#When the Person model instantiates, self.The "value" of the field is assigned to name
#At the same time, the class variable "Person"."name" is "self".Cannot be referenced by "name"
#Therefore, "Person._meta.I wonder if they are evacuating to "fields"?
--About Python metaclasses -Understanding Python metaprogramming (metaclass, metaclass) -Django model and field class variables
Recommended Posts