Django/モデルインポート時の処理を読む(メタ情報構築)
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Djangoを読む]]
#contents
*はじめに [#j8694acf]
前回飛ばした部分、モデルをインポートする際に何が行われて...
#code(Python){{
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=mode...
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
}}
*django/db/models [#xfb3fcb5]
ともかく、django.db.modelsを見てみましょう。__init__.py、...
#code(Python){{
from django.db.models.fields import * # NOQA
from django.db.models.base import DEFERRED, Model # NOQA...
from django.db.models.fields.related import ( # NOQA iso...
ForeignKey, ForeignObject, OneToOneField, ManyToManyF...
ManyToOneRel, ManyToManyRel, OneToOneRel,
)
}}
このことから、
-Modelはbaseモジュール
-ForeignKeyはfields.relatedモジュール
-CharFieldなどはfieldsモジュールに書かれているだろう
ということがわかります。
*django/db/models/base.py [#cdb008c3]
まずはModelクラスの定義から見てみます。というものの今回注...
#code(Python){{
class Model(six.with_metaclass(ModelBase)):
}}
Python2でも3でも実行できるようにsixが使われていますが、メ...
**ModelBase [#p0bdff75]
というわけでModelBaseの__new__メソッドを見ます。200行以上...
#code(Python){{
def __new__(cls, name, bases, attrs):
super_new = super(ModelBase, cls).__new__
# Also ensure initialization is only performed fo...
# (excluding Model class itself).
parents = [b for b in bases if isinstance(b, Mode...
if not parents:
return super_new(cls, name, bases, attrs)
# Create the class.
module = attrs.pop('__module__')
new_class = super_new(cls, name, bases, {'__modul...
}}
先頭部分。クラスを作成しています。ただし、渡されたattrsを...
次に[[メタデータ>https://docs.djangoproject.com/ja/1.10/r...
で、続き。
#code(Python){{
new_class.add_to_class('_meta', Options(meta, app...
}}
前回も出てきた_metaの正体はOptionsインスタンスのようです...
add_to_classメソッド。
#code(Python){{
def add_to_class(cls, name, value):
# We should call the contribute_to_class method o...
if not inspect.isclass(value) and hasattr(value, ...
value.contribute_to_class(cls, name)
else:
setattr(cls, name, value)
}}
というわけで、クラスへの追加を行う際にcontribute_to_class...
Optionsクラスのcontribute_to_classでは名前の設定とかMata...
#code(Python){{
def contribute_to_class(self, cls, name):
# 省略
self.object_name = cls.__name__
self.model_name = self.object_name.lower()
# 省略
# If the db_table wasn't provided, use the app_la...
if not self.db_table:
self.db_table = "%s_%s" % (self.app_label, se...
self.db_table = truncate_name(self.db_table, ...
}}
ということでアプリ名とモデル名(の小文字)からDBのテーブ...
ModelBaseの__new__に戻って、次の例外とかを登録している箇...
その次、
#code(Python){{
# Add all attributes to the class.
for obj_name, obj in attrs.items():
new_class.add_to_class(obj_name, obj)
}}
クラス定義中に書かれている変数やメソッドはattrs(第4引数...
#code(Python){{
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
}}
みたいなフィールドオブジェクトですね。こっちに行くと長く...
と言っても、後書いてあるのはプロキシとか継承とかに関する...
#code(Python){{
new_class._prepare()
new_class._meta.apps.register_model(new_class._me...
return new_class
}}
appsに登録が行われていますね。これにより前回謎だった「い...
*django/db/models/fields [#jf71dbcc]
それではフィールドクラスです。予想通り、CharFieldもDateTi...
#code(Python){{
def contribute_to_class(self, cls, name, private_only...
"""
Register the field with the model class it belong...
If private_only is True, a separate instance of t...
created for every subclass of cls, even if cls is...
model.
"""
self.set_attributes_from_name(name)
self.model = cls
if private_only:
cls._meta.add_field(self, private=True)
else:
cls._meta.add_field(self)
if self.column:
# Don't override classmethods with the descri...
# if you have a classmethod and a field with ...
# such fields can't be deferred (we don't hav...
if not getattr(cls, self.attname, None):
setattr(cls, self.attname, DeferredAttrib...
}}
やってることは2つ
-_meta(Optionsオブジェクト)にフィールド登録
-モデルクラスにDeferredAttribute登録(columnとかattnameと...
add_fieldメソッド(一部省略)
#code(Python){{
def add_field(self, field, private=False, virtual=NOT...
# Insert the given field in the order in which it...
# the "creation_counter" attribute of the field.
# Move many-to-many related fields from self.fiel...
# self.many_to_many.
if private:
self.private_fields.append(field)
elif field.is_relation and field.many_to_many:
self.local_many_to_many.insert(bisect(self.lo...
else:
self.local_fields.insert(bisect(self.local_fi...
}}
というわけでlocal_fieldsに追加されます。bisectは配列中の...
*django/db/models/fields/related.py [#lffffc5c]
関連についても確認しましょう。まずは関連を設定していると...
#code(Python){{
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=mode...
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
}}
ForeignKeyはrelatedモジュールに記述されています。まず継承...
#code(Python){{
class ForeignKey(ForeignObject):
"""
Provide a many-to-one relation by adding a column to ...
to hold the remote value.
By default ForeignKey will target the pk of the remot...
behavior can be changed by using the ``to_field`` arg...
"""
class ForeignObject(RelatedField):
"""
Abstraction of the ForeignKey relation, supports mult...
"""
class RelatedField(Field):
"""
Base class that all relational fields inherit from.
"""
}}
めんどくさい(笑)。ともかく関連について確認するにはこれら...
ForeignKeyの__init__を見てみましょう。とりあえずon_delete...
#code(Python){{
def __init__(self, to, on_delete=None, related_name=N...
limit_choices_to=None, parent_link=False...
db_constraint=True, **kwargs):
try:
to._meta.model_name
except AttributeError:
# 省略
else:
# For backwards compatibility purposes, we ne...
# the to_field during FK construction. It won...
# be correct until contribute_to_class is cal...
to_field = to_field or (to._meta.pk and to._m...
# on_deleteに関する処理
kwargs['rel'] = self.rel_class(
self, to, to_field,
related_name=related_name,
related_query_name=related_query_name,
limit_choices_to=limit_choices_to,
parent_link=parent_link,
on_delete=on_delete,
)
kwargs['db_index'] = kwargs.get('db_index', True)
super(ForeignKey, self).__init__(
to, on_delete, from_fields=['self'], to_field...
self.db_constraint = db_constraint
}}
to_fieldは指定してないのでtoのモデル(Question)のpkが取...
次に、rel_classのインスタンスを作ってキーワード引数のrel...
ManyToOneRelクラスはreverse_relatedモジュールに書かれてい...
#code(Python){{
class ManyToOneRel(ForeignObjectRel):
def __init__(self, field, to, field_name, related_nam...
limit_choices_to=None, parent_link=False...
super(ManyToOneRel, self).__init__(
field, to,
related_name=related_name,
related_query_name=related_query_name,
limit_choices_to=limit_choices_to,
parent_link=parent_link,
on_delete=on_delete,
)
self.field_name = field_name
class ForeignObjectRel(object):
def __init__(self, field, to, related_name=None, rela...
limit_choices_to=None, parent_link=False...
self.field = field
self.model = to
self.related_name = related_name
self.related_query_name = related_query_name
self.limit_choices_to = {} if limit_choices_to is...
self.parent_link = parent_link
self.on_delete = on_delete
self.symmetrical = False
self.multiple = True
}}
というわけでmodelとしてtoで渡されたクラスが指定されていま...
*_prepareでの処理 [#zed52b7d]
さて、モデルがインポートされたときにappsに登録、フィール...
**ModelBase._prepare [#y0c38dba]
#code(Python){{
def _prepare(cls):
"""
Creates some methods once self._meta has been pop...
"""
opts = cls._meta
opts._prepare(cls)
# 省略
if not opts.managers or cls._requires_legacy_defa...
if any(f.name == 'objects' for f in opts.fiel...
raise ValueError(
"Model %s must specify a custom Manag...
"field named 'objects'." % cls.__name__
)
manager = Manager()
manager.auto_created = True
cls.add_to_class('objects', manager)
signals.class_prepared.send(sender=cls)
}}
objectsを登録しています。これにより、Question.objects.get...
managerの作りもまたややこしいですけど、そこを見るのはまた...
**Options._prepare [#d8f39be7]
_meta、Optionsクラスの方の_prepareを見てみましょう。
#code(Python){{
def _prepare(self, model):
# 省略
if self.pk is None:
if self.parents:
# 省略
else:
auto = AutoField(verbose_name='ID', prima...
model.add_to_class('id', auto)
}}
というわけでidフィールドが設定されています。
ところで、モデルのdocstringを見てみると以下のようにidが先...
>>> print(Question.__doc__)
Question(id, question_text, pub_date)
add_fieldの際に、appendではなくinsertを使い、さらに挿入位...
#code(Python){{
class Field(RegisterLookupMixin):
creation_counter = 0
auto_creation_counter = -1
def __init__(self, verbose_name=None, name=None, prim...
max_length=None, unique=False, blank=Fal...
db_index=False, rel=None, default=NOT_PR...
serialize=True, unique_for_date=None, un...
unique_for_year=None, choices=None, help...
db_tablespace=None, auto_created=False, ...
error_messages=None):
if auto_created:
self.creation_counter = Field.auto_creation_c...
Field.auto_creation_counter -= 1
else:
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
}}
というわけで、auto_createdの場合はマイナスの値が割り振ら...
*おわりに [#s0d866b3]
今回はモデルインポート時の処理、モデルの自己登録とメタ情...
終了行:
[[Djangoを読む]]
#contents
*はじめに [#j8694acf]
前回飛ばした部分、モデルをインポートする際に何が行われて...
#code(Python){{
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=mode...
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
}}
*django/db/models [#xfb3fcb5]
ともかく、django.db.modelsを見てみましょう。__init__.py、...
#code(Python){{
from django.db.models.fields import * # NOQA
from django.db.models.base import DEFERRED, Model # NOQA...
from django.db.models.fields.related import ( # NOQA iso...
ForeignKey, ForeignObject, OneToOneField, ManyToManyF...
ManyToOneRel, ManyToManyRel, OneToOneRel,
)
}}
このことから、
-Modelはbaseモジュール
-ForeignKeyはfields.relatedモジュール
-CharFieldなどはfieldsモジュールに書かれているだろう
ということがわかります。
*django/db/models/base.py [#cdb008c3]
まずはModelクラスの定義から見てみます。というものの今回注...
#code(Python){{
class Model(six.with_metaclass(ModelBase)):
}}
Python2でも3でも実行できるようにsixが使われていますが、メ...
**ModelBase [#p0bdff75]
というわけでModelBaseの__new__メソッドを見ます。200行以上...
#code(Python){{
def __new__(cls, name, bases, attrs):
super_new = super(ModelBase, cls).__new__
# Also ensure initialization is only performed fo...
# (excluding Model class itself).
parents = [b for b in bases if isinstance(b, Mode...
if not parents:
return super_new(cls, name, bases, attrs)
# Create the class.
module = attrs.pop('__module__')
new_class = super_new(cls, name, bases, {'__modul...
}}
先頭部分。クラスを作成しています。ただし、渡されたattrsを...
次に[[メタデータ>https://docs.djangoproject.com/ja/1.10/r...
で、続き。
#code(Python){{
new_class.add_to_class('_meta', Options(meta, app...
}}
前回も出てきた_metaの正体はOptionsインスタンスのようです...
add_to_classメソッド。
#code(Python){{
def add_to_class(cls, name, value):
# We should call the contribute_to_class method o...
if not inspect.isclass(value) and hasattr(value, ...
value.contribute_to_class(cls, name)
else:
setattr(cls, name, value)
}}
というわけで、クラスへの追加を行う際にcontribute_to_class...
Optionsクラスのcontribute_to_classでは名前の設定とかMata...
#code(Python){{
def contribute_to_class(self, cls, name):
# 省略
self.object_name = cls.__name__
self.model_name = self.object_name.lower()
# 省略
# If the db_table wasn't provided, use the app_la...
if not self.db_table:
self.db_table = "%s_%s" % (self.app_label, se...
self.db_table = truncate_name(self.db_table, ...
}}
ということでアプリ名とモデル名(の小文字)からDBのテーブ...
ModelBaseの__new__に戻って、次の例外とかを登録している箇...
その次、
#code(Python){{
# Add all attributes to the class.
for obj_name, obj in attrs.items():
new_class.add_to_class(obj_name, obj)
}}
クラス定義中に書かれている変数やメソッドはattrs(第4引数...
#code(Python){{
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
}}
みたいなフィールドオブジェクトですね。こっちに行くと長く...
と言っても、後書いてあるのはプロキシとか継承とかに関する...
#code(Python){{
new_class._prepare()
new_class._meta.apps.register_model(new_class._me...
return new_class
}}
appsに登録が行われていますね。これにより前回謎だった「い...
*django/db/models/fields [#jf71dbcc]
それではフィールドクラスです。予想通り、CharFieldもDateTi...
#code(Python){{
def contribute_to_class(self, cls, name, private_only...
"""
Register the field with the model class it belong...
If private_only is True, a separate instance of t...
created for every subclass of cls, even if cls is...
model.
"""
self.set_attributes_from_name(name)
self.model = cls
if private_only:
cls._meta.add_field(self, private=True)
else:
cls._meta.add_field(self)
if self.column:
# Don't override classmethods with the descri...
# if you have a classmethod and a field with ...
# such fields can't be deferred (we don't hav...
if not getattr(cls, self.attname, None):
setattr(cls, self.attname, DeferredAttrib...
}}
やってることは2つ
-_meta(Optionsオブジェクト)にフィールド登録
-モデルクラスにDeferredAttribute登録(columnとかattnameと...
add_fieldメソッド(一部省略)
#code(Python){{
def add_field(self, field, private=False, virtual=NOT...
# Insert the given field in the order in which it...
# the "creation_counter" attribute of the field.
# Move many-to-many related fields from self.fiel...
# self.many_to_many.
if private:
self.private_fields.append(field)
elif field.is_relation and field.many_to_many:
self.local_many_to_many.insert(bisect(self.lo...
else:
self.local_fields.insert(bisect(self.local_fi...
}}
というわけでlocal_fieldsに追加されます。bisectは配列中の...
*django/db/models/fields/related.py [#lffffc5c]
関連についても確認しましょう。まずは関連を設定していると...
#code(Python){{
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=mode...
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
}}
ForeignKeyはrelatedモジュールに記述されています。まず継承...
#code(Python){{
class ForeignKey(ForeignObject):
"""
Provide a many-to-one relation by adding a column to ...
to hold the remote value.
By default ForeignKey will target the pk of the remot...
behavior can be changed by using the ``to_field`` arg...
"""
class ForeignObject(RelatedField):
"""
Abstraction of the ForeignKey relation, supports mult...
"""
class RelatedField(Field):
"""
Base class that all relational fields inherit from.
"""
}}
めんどくさい(笑)。ともかく関連について確認するにはこれら...
ForeignKeyの__init__を見てみましょう。とりあえずon_delete...
#code(Python){{
def __init__(self, to, on_delete=None, related_name=N...
limit_choices_to=None, parent_link=False...
db_constraint=True, **kwargs):
try:
to._meta.model_name
except AttributeError:
# 省略
else:
# For backwards compatibility purposes, we ne...
# the to_field during FK construction. It won...
# be correct until contribute_to_class is cal...
to_field = to_field or (to._meta.pk and to._m...
# on_deleteに関する処理
kwargs['rel'] = self.rel_class(
self, to, to_field,
related_name=related_name,
related_query_name=related_query_name,
limit_choices_to=limit_choices_to,
parent_link=parent_link,
on_delete=on_delete,
)
kwargs['db_index'] = kwargs.get('db_index', True)
super(ForeignKey, self).__init__(
to, on_delete, from_fields=['self'], to_field...
self.db_constraint = db_constraint
}}
to_fieldは指定してないのでtoのモデル(Question)のpkが取...
次に、rel_classのインスタンスを作ってキーワード引数のrel...
ManyToOneRelクラスはreverse_relatedモジュールに書かれてい...
#code(Python){{
class ManyToOneRel(ForeignObjectRel):
def __init__(self, field, to, field_name, related_nam...
limit_choices_to=None, parent_link=False...
super(ManyToOneRel, self).__init__(
field, to,
related_name=related_name,
related_query_name=related_query_name,
limit_choices_to=limit_choices_to,
parent_link=parent_link,
on_delete=on_delete,
)
self.field_name = field_name
class ForeignObjectRel(object):
def __init__(self, field, to, related_name=None, rela...
limit_choices_to=None, parent_link=False...
self.field = field
self.model = to
self.related_name = related_name
self.related_query_name = related_query_name
self.limit_choices_to = {} if limit_choices_to is...
self.parent_link = parent_link
self.on_delete = on_delete
self.symmetrical = False
self.multiple = True
}}
というわけでmodelとしてtoで渡されたクラスが指定されていま...
*_prepareでの処理 [#zed52b7d]
さて、モデルがインポートされたときにappsに登録、フィール...
**ModelBase._prepare [#y0c38dba]
#code(Python){{
def _prepare(cls):
"""
Creates some methods once self._meta has been pop...
"""
opts = cls._meta
opts._prepare(cls)
# 省略
if not opts.managers or cls._requires_legacy_defa...
if any(f.name == 'objects' for f in opts.fiel...
raise ValueError(
"Model %s must specify a custom Manag...
"field named 'objects'." % cls.__name__
)
manager = Manager()
manager.auto_created = True
cls.add_to_class('objects', manager)
signals.class_prepared.send(sender=cls)
}}
objectsを登録しています。これにより、Question.objects.get...
managerの作りもまたややこしいですけど、そこを見るのはまた...
**Options._prepare [#d8f39be7]
_meta、Optionsクラスの方の_prepareを見てみましょう。
#code(Python){{
def _prepare(self, model):
# 省略
if self.pk is None:
if self.parents:
# 省略
else:
auto = AutoField(verbose_name='ID', prima...
model.add_to_class('id', auto)
}}
というわけでidフィールドが設定されています。
ところで、モデルのdocstringを見てみると以下のようにidが先...
>>> print(Question.__doc__)
Question(id, question_text, pub_date)
add_fieldの際に、appendではなくinsertを使い、さらに挿入位...
#code(Python){{
class Field(RegisterLookupMixin):
creation_counter = 0
auto_creation_counter = -1
def __init__(self, verbose_name=None, name=None, prim...
max_length=None, unique=False, blank=Fal...
db_index=False, rel=None, default=NOT_PR...
serialize=True, unique_for_date=None, un...
unique_for_year=None, choices=None, help...
db_tablespace=None, auto_created=False, ...
error_messages=None):
if auto_created:
self.creation_counter = Field.auto_creation_c...
Field.auto_creation_counter -= 1
else:
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
}}
というわけで、auto_createdの場合はマイナスの値が割り振ら...
*おわりに [#s0d866b3]
今回はモデルインポート時の処理、モデルの自己登録とメタ情...
ページ名: