Django/モデル保存時の処理を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Djangoを読む]]
#contents
*はじめに [#m2539ede]
ようやくマイグレーションが終わりDjangoのモデルを作成、保...
>>> from polls.models import Question, Choice
# Create a new Question.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=t...
# Save the object into the database. You have to call sa...
>>> q.save()
少しずつ見ていきましょうということでとりあえずここまで。
念のため、Questionの定義
#code(Python){{
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
}}
*django/db/models/base.py [#z9a85554]
というわけでModelクラスを見ていきます。modelsの__init__.p...
**Model.__init__ [#d5de0184]
インスタンスを作成しているのでまずはもちろん__init__メソ...
#code(Python){{
def __init__(self, *args, **kwargs):
# Set up the storage for instance state
self._state = ModelState()
if not kwargs:
# 省略
else:
# Slower, kwargs-ready version.
fields_iter = iter(self._meta.fields)
# Now we're left with the unprocessed fields that...
# keywords, or default.
for field in fields_iter:
is_related_object = False
# Virtual field
if field.attname not in kwargs and field.colu...
continue
if kwargs:
if isinstance(field.remote_field, Foreign...
# 省略
else:
try:
val = kwargs.pop(field.attname)
except KeyError:
# 省略
else:
val = field.get_default()
if is_related_object:
# 省略
else:
if val is not DEFERRED:
setattr(self, field.attname, val)
super(Model, self).__init__()
}}
何をしているかというと、
+メタ情報として持っている各フィールドについて
+キーワード引数から値を取得し
+インスタンスの属性として設定
を行っています。普通のクラスでやるような初期化をメタ情報...
**save [#ed7ba9ac]
というわけでモデルのインスタンス化ができたので次は保存を...
#code(Python){{
def save(self, force_insert=False, force_update=False...
update_fields=None):
using = using or router.db_for_write(self.__class...
self.save_base(using=using, force_insert=force_in...
force_update=force_update, update_...
}}
普通に使っている分には結局この2行だけになります。usingは...
save_baseメソッドも例によって関係のあるところだけ抜き出し
#code(Python){{
def save_base(self, raw=False, force_insert=False,
force_update=False, using=None, update_...
cls = self.__class__
with transaction.atomic(using=using, savepoint=Fa...
updated = self._save_table(raw, cls, force_in...
# Store the database on which the object was saved
self._state.db = using
# Once saved, this is no longer a to-be-added ins...
self._state.adding = False
}}
_save_tableに続きます。tableという名前が出てきたことから...
**_save_table [#p8be7177]
重要そうなのでセクションを変えて、_save_tableメソッドです...
#code(Python){{
def _save_table(self, raw=False, cls=None, force_inse...
force_update=False, using=None, updat...
"""
Does the heavy-lifting involved in saving. Update...
for a single table.
"""
meta = cls._meta
pk_val = self._get_pk_val(meta)
if pk_val is None:
pk_val = meta.pk.get_pk_value_on_save(self)
setattr(self, meta.pk.attname, pk_val)
pk_set = pk_val is not None
updated = False
# If possible, try an UPDATE. If that doesn't upd...
if not updated:
fields = meta.local_concrete_fields
if not pk_set:
fields = [f for f in fields if not isinst...
update_pk = bool(meta.has_auto_field and not ...
result = self._do_insert(cls._base_manager, u...
if update_pk:
setattr(self, meta.pk.attname, result)
return updated
}}
_get_pk_valメソッドを呼び出してpk、主キーを取得しています...
_do_insertメソッドはシンプル、というかmanagerへの移譲が行...
#code(Python){{
def _do_insert(self, manager, using, fields, update_p...
"""
Do an INSERT. If update_pk is defined then this m...
the new pk for the model.
"""
return manager._insert([self], fields=fields, ret...
using=using, raw=raw)
}}
**ModelBase._base_manager [#ub941a8b]
ところで、manager、より正確には_base_managerとは何者なの...
#code(Python){{
@property
def _base_manager(cls):
return cls._meta.base_manager
}}
_meta、optionsモジュールのOptionsクラスに続く。base_manag...
#code(Python){{
@cached_property
def base_manager(self):
manager = Manager()
manager.name = '_base_manager'
manager.model = self.model
manager.auto_created = True
return manager
}}
というわけで、managerモジュールのManagerクラスのインスタ...
ところで、managerという単語は前にも出てきた気がします。具...
#code(Python){{
def _prepare(cls):
# 省略
if not opts.managers or cls._requires_legacy_defa...
manager = Manager()
manager.auto_created = True
cls.add_to_class('objects', manager)
}}
base_managerプロパティを見たとき、「ややこしい処理には行...
*django/db/models/manager.py [#d4017ab9]
さて、Managerクラスです。
#code(Python){{
class Manager(BaseManager.from_queryset(QuerySet)):
pass
}}
・・・。BaseManagerのfrom_querysetメソッド
#code(Python){{
@classmethod
def from_queryset(cls, queryset_class, class_name=Non...
if class_name is None:
class_name = '%sFrom%s' % (cls.__name__, quer...
class_dict = {
'_queryset_class': queryset_class,
}
class_dict.update(cls._get_queryset_methods(query...
return type(class_name, (cls,), class_dict)
}}
_get_queryset_methodsでQuerySetからメソッドを取得し、新し...
#code(Python){{
@classmethod
def _get_queryset_methods(cls, queryset_class):
def create_method(name, method):
def manager_method(self, *args, **kwargs):
return getattr(self.get_queryset(), name)...
manager_method.__name__ = method.__name__
manager_method.__doc__ = method.__doc__
return manager_method
new_methods = {}
# Refs http://bugs.python.org/issue1785.
predicate = inspect.isfunction if six.PY3 else in...
for name, method in inspect.getmembers(queryset_c...
# Only copy missing methods.
if hasattr(cls, name):
continue
# Only copy public methods or methods with th...
queryset_only = getattr(method, 'queryset_onl...
if queryset_only or (queryset_only is None an...
continue
# Copy the method onto the manager.
new_methods[name] = create_method(name, method)
return new_methods
}}
うーん、関数内関数内関数なんて初めて見た(笑)
get_querysetメソッド。なんで毎回別のオブジェクト作ってる...
#code(Python){{
def get_queryset(self):
"""
Returns a new QuerySet object. Subclasses can ov...
easily customize the behavior of the Manager.
"""
return self._queryset_class(model=self.model, usi...
}}
*django/db/models/query.py [#cbf73b18]
さて、話を戻して、_insertメソッドです。上で見てきたように...
#code(Python){{
def _insert(self, objs, fields, return_id=False, raw=...
"""
Inserts a new record for the given model. This pr...
the InsertQuery class and is how Model.save() is ...
"""
self._for_write = True
if using is None:
using = self.db
query = sql.InsertQuery(self.model)
query.insert_values(fields, objs, raw=raw)
return query.get_compiler(using=using).execute_sq...
_insert.alters_data = True
_insert.queryset_only = False
}}
Pythonってメソッドに属性を設定できるんですね。まあ普通使...
*django/db/models/sql [#lf888e03]
sqlモジュールに移りましょう。まずは__init__。
#code(Python){{
from django.db.models.sql.datastructures import EmptyResu...
from django.db.models.sql.query import * # NOQA
from django.db.models.sql.subqueries import * # NOQA
from django.db.models.sql.where import AND, OR
__all__ = ['Query', 'AND', 'OR', 'EmptyResultSet']
}}
InsertQueryはqueryモジュールに書かれていそうです。
・・・と思ったらsubqueriesの方に書いてありました。このsub...
**django.db.models.sql.query.Query.get_compiler [#se5beaab]
というわけでまずget_compilerから見てみましょう。
#code(Python){{
def get_compiler(self, using=None, connection=None):
if using:
connection = connections[using]
return connection.ops.compiler(self.compiler)(sel...
}}
出てきましたconnections。ここで前回も見てきた各DBMSに対応...
といっても、ops、sqlite3の方のDatabaseOperationsインスタ...
#code(Python){{
compiler_module = "django.db.models.sql.compiler"
def compiler(self, compiler_name):
"""
Returns the SQLCompiler class corresponding to th...
in the namespace corresponding to the `compiler_m...
on this backend.
"""
if self._cache is None:
self._cache = import_module(self.compiler_mod...
return getattr(self._cache, compiler_name)
}}
compiler_name、その元Queryインスタンスのcompiler、はInser...
**django.db.models.sql.compiler.SQLInsertCompiler.execute...
残りはexecute_sqlメソッドです。
#code(Python){{
def execute_sql(self, return_id=False):
self.return_id = return_id
with self.connection.cursor() as cursor:
for sql, params in self.as_sql():
cursor.execute(sql, params)
if self.connection.features.can_return_ids_fr...
return self.connection.ops.fetch_returned...
if self.connection.features.can_return_id_fro...
assert len(self.query.objs) == 1
return self.connection.ops.fetch_returned...
return self.connection.ops.last_insert_id(
cursor, self.query.get_meta().db_table, s...
)
}}
先に言っておくと、sqlite3の場合、can_return系のfeaturesは...
***as_sql [#xaf436f3]
as_sqlメソッドではDBMSのfeaturesも考慮してINSERT文が構築...
#code(Python){{
def as_sql(self):
# We don't need quote_name_unless_alias() here, s...
# going to be column names (so we can avoid the e...
qn = self.connection.ops.quote_name
opts = self.query.get_meta()
result = ['INSERT INTO %s' % qn(opts.db_table)]
has_fields = bool(self.query.fields)
fields = self.query.fields if has_fields else [op...
result.append('(%s)' % ', '.join(qn(f.column) for...
if has_fields:
value_rows = [
[self.prepare_value(field, self.pre_save_...
for obj in self.query.objs
]
else:
# 省略
# Currently the backends just accept values when ...
# queries and generate their own placeholders. Do...
# necessary and it should be possible to use plac...
# expressions in bulk inserts too.
can_bulk = (not self.return_id and self.connectio...
placeholder_rows, param_rows = self.assemble_as_s...
if can_bulk:
# 省略
else:
return [
(" ".join(result + ["VALUES (%s)" % ", "....
for p, vals in zip(placeholder_rows, para...
]
}}
pre_save_valメソッドはコメントにあるようにFieldクラスを呼...
#code(Python){{
def pre_save_val(self, field, obj):
"""
Get the given field's value off the given obj. pr...
things like auto_now on DateTimeField. Skip it if...
"""
if self.query.raw:
return getattr(obj, field.attname)
return field.pre_save(obj, add=True)
}}
prepare_value、assemble_as_sqlについて見ていきます。
***prepare_value [#k63fa07e]
prepare_valueメソッド。resolve_expressionは設定されていな...
#code(Python){{
def prepare_value(self, field, value):
"""
Prepare a value to be used in a query by resolvin...
expression and otherwise calling the field's get_...
"""
if hasattr(value, 'resolve_expression'):
# 省略
else:
value = field.get_db_prep_save(value, connect...
return value
}}
Fieldのget_db_prep_saveメソッド。は、get_db_prep_valueメ...
#code(Python){{
def get_db_prep_save(self, value, connection):
"""
Returns field's value prepared for saving into a ...
"""
return self.get_db_prep_value(value, connection=c...
prepared=False)
def get_db_prep_value(self, value, connection, prepar...
"""Returns field's value prepared for interacting...
backend.
Used by the default implementations of get_db_pre...
"""
if not prepared:
value = self.get_prep_value(value)
return value
}}
get_prep_valueメソッドはFieldクラスの各サブクラスでオーバ...
#code(Python){{
def get_prep_value(self, value):
value = super(DateTimeField, self).get_prep_value...
value = self.to_python(value)
if value is not None and settings.USE_TZ and time...
# 省略
return value
}}
***assemble_as_sql [#b25aee66]
SQLInsertCompilerクラスに戻って、assemble_as_sqlメソッド...
#code(Python){{
def assemble_as_sql(self, fields, value_rows):
"""
Take a sequence of N fields and a sequence of M r...
generate placeholder SQL and parameters for each ...
return a pair containing:
* a sequence of M rows of N SQL placeholder stri...
* a sequence of M rows of corresponding paramete...
Each placeholder string may contain any number of...
strings, and each parameter row will contain exac...
as the total number of '%s's in the corresponding...
"""
if not value_rows:
return [], []
# list of (sql, [params]) tuples for each object ...
# Shape: [n_objs][n_fields][2]
rows_of_fields_as_sql = (
(self.field_as_sql(field, v) for field, v in ...
for row in value_rows
)
# tuple like ([sqls], [[params]s]) for each objec...
# Shape: [n_objs][2][n_fields]
sql_and_param_pair_rows = (zip(*row) for row in r...
# Extract separate lists for placeholders and par...
# Each of these has shape [n_objs][n_fields]
placeholder_rows, param_rows = zip(*sql_and_param...
# Params for each field are still lists, and need...
param_rows = [[p for ps in row for p in ps] for r...
return placeholder_rows, param_rows
}}
見た感じ、何回も形式変換を行っています。とりあえず、field...
#code(Python){{
def field_as_sql(self, field, val):
"""
Take a field and a value intended to be saved on ...
return placeholder SQL and accompanying params. C...
expressions and fields with get_placeholder() def...
When field is None, the value is considered raw a...
placeholder, with no corresponding parameters ret...
"""
if field is None:
# 省略
elif hasattr(val, 'as_sql'):
# 省略
elif hasattr(field, 'get_placeholder'):
# 省略
else:
# Return the common case for the placeholder
sql, params = '%s', [val]
return sql, params
}}
というわけでまずrows_of_fields_as_sqlは、
('%s', [挿入する値])
のタプルのリスト(正確にはジェネレータ)になります。さら...
sql_and_param_pair_rowsは各オブジェクト(テーブルの一行)...
さらにzipを通すことで、placeholder_rowsとparam_rowsに分離...
placeholder_rows
(('%s', '%s'),)
param_rows
(([値], [値]),)
うーん、だんだんよくわからなくなってきた(上の形状は確認...
param_rows
[[値, 値]]
というリストに変換されます。このようにして返されたフィー...
*おわりに [#zcdd226f]
今回はモデル処理の手始めとしてモデルの保存について見てき...
また、SQLを組み立てるのにCompilerというオブジェクトが出て...
というわけで引き続き、「APIで遊んでみる」を見ていきましょ...
終了行:
[[Djangoを読む]]
#contents
*はじめに [#m2539ede]
ようやくマイグレーションが終わりDjangoのモデルを作成、保...
>>> from polls.models import Question, Choice
# Create a new Question.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=t...
# Save the object into the database. You have to call sa...
>>> q.save()
少しずつ見ていきましょうということでとりあえずここまで。
念のため、Questionの定義
#code(Python){{
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
}}
*django/db/models/base.py [#z9a85554]
というわけでModelクラスを見ていきます。modelsの__init__.p...
**Model.__init__ [#d5de0184]
インスタンスを作成しているのでまずはもちろん__init__メソ...
#code(Python){{
def __init__(self, *args, **kwargs):
# Set up the storage for instance state
self._state = ModelState()
if not kwargs:
# 省略
else:
# Slower, kwargs-ready version.
fields_iter = iter(self._meta.fields)
# Now we're left with the unprocessed fields that...
# keywords, or default.
for field in fields_iter:
is_related_object = False
# Virtual field
if field.attname not in kwargs and field.colu...
continue
if kwargs:
if isinstance(field.remote_field, Foreign...
# 省略
else:
try:
val = kwargs.pop(field.attname)
except KeyError:
# 省略
else:
val = field.get_default()
if is_related_object:
# 省略
else:
if val is not DEFERRED:
setattr(self, field.attname, val)
super(Model, self).__init__()
}}
何をしているかというと、
+メタ情報として持っている各フィールドについて
+キーワード引数から値を取得し
+インスタンスの属性として設定
を行っています。普通のクラスでやるような初期化をメタ情報...
**save [#ed7ba9ac]
というわけでモデルのインスタンス化ができたので次は保存を...
#code(Python){{
def save(self, force_insert=False, force_update=False...
update_fields=None):
using = using or router.db_for_write(self.__class...
self.save_base(using=using, force_insert=force_in...
force_update=force_update, update_...
}}
普通に使っている分には結局この2行だけになります。usingは...
save_baseメソッドも例によって関係のあるところだけ抜き出し
#code(Python){{
def save_base(self, raw=False, force_insert=False,
force_update=False, using=None, update_...
cls = self.__class__
with transaction.atomic(using=using, savepoint=Fa...
updated = self._save_table(raw, cls, force_in...
# Store the database on which the object was saved
self._state.db = using
# Once saved, this is no longer a to-be-added ins...
self._state.adding = False
}}
_save_tableに続きます。tableという名前が出てきたことから...
**_save_table [#p8be7177]
重要そうなのでセクションを変えて、_save_tableメソッドです...
#code(Python){{
def _save_table(self, raw=False, cls=None, force_inse...
force_update=False, using=None, updat...
"""
Does the heavy-lifting involved in saving. Update...
for a single table.
"""
meta = cls._meta
pk_val = self._get_pk_val(meta)
if pk_val is None:
pk_val = meta.pk.get_pk_value_on_save(self)
setattr(self, meta.pk.attname, pk_val)
pk_set = pk_val is not None
updated = False
# If possible, try an UPDATE. If that doesn't upd...
if not updated:
fields = meta.local_concrete_fields
if not pk_set:
fields = [f for f in fields if not isinst...
update_pk = bool(meta.has_auto_field and not ...
result = self._do_insert(cls._base_manager, u...
if update_pk:
setattr(self, meta.pk.attname, result)
return updated
}}
_get_pk_valメソッドを呼び出してpk、主キーを取得しています...
_do_insertメソッドはシンプル、というかmanagerへの移譲が行...
#code(Python){{
def _do_insert(self, manager, using, fields, update_p...
"""
Do an INSERT. If update_pk is defined then this m...
the new pk for the model.
"""
return manager._insert([self], fields=fields, ret...
using=using, raw=raw)
}}
**ModelBase._base_manager [#ub941a8b]
ところで、manager、より正確には_base_managerとは何者なの...
#code(Python){{
@property
def _base_manager(cls):
return cls._meta.base_manager
}}
_meta、optionsモジュールのOptionsクラスに続く。base_manag...
#code(Python){{
@cached_property
def base_manager(self):
manager = Manager()
manager.name = '_base_manager'
manager.model = self.model
manager.auto_created = True
return manager
}}
というわけで、managerモジュールのManagerクラスのインスタ...
ところで、managerという単語は前にも出てきた気がします。具...
#code(Python){{
def _prepare(cls):
# 省略
if not opts.managers or cls._requires_legacy_defa...
manager = Manager()
manager.auto_created = True
cls.add_to_class('objects', manager)
}}
base_managerプロパティを見たとき、「ややこしい処理には行...
*django/db/models/manager.py [#d4017ab9]
さて、Managerクラスです。
#code(Python){{
class Manager(BaseManager.from_queryset(QuerySet)):
pass
}}
・・・。BaseManagerのfrom_querysetメソッド
#code(Python){{
@classmethod
def from_queryset(cls, queryset_class, class_name=Non...
if class_name is None:
class_name = '%sFrom%s' % (cls.__name__, quer...
class_dict = {
'_queryset_class': queryset_class,
}
class_dict.update(cls._get_queryset_methods(query...
return type(class_name, (cls,), class_dict)
}}
_get_queryset_methodsでQuerySetからメソッドを取得し、新し...
#code(Python){{
@classmethod
def _get_queryset_methods(cls, queryset_class):
def create_method(name, method):
def manager_method(self, *args, **kwargs):
return getattr(self.get_queryset(), name)...
manager_method.__name__ = method.__name__
manager_method.__doc__ = method.__doc__
return manager_method
new_methods = {}
# Refs http://bugs.python.org/issue1785.
predicate = inspect.isfunction if six.PY3 else in...
for name, method in inspect.getmembers(queryset_c...
# Only copy missing methods.
if hasattr(cls, name):
continue
# Only copy public methods or methods with th...
queryset_only = getattr(method, 'queryset_onl...
if queryset_only or (queryset_only is None an...
continue
# Copy the method onto the manager.
new_methods[name] = create_method(name, method)
return new_methods
}}
うーん、関数内関数内関数なんて初めて見た(笑)
get_querysetメソッド。なんで毎回別のオブジェクト作ってる...
#code(Python){{
def get_queryset(self):
"""
Returns a new QuerySet object. Subclasses can ov...
easily customize the behavior of the Manager.
"""
return self._queryset_class(model=self.model, usi...
}}
*django/db/models/query.py [#cbf73b18]
さて、話を戻して、_insertメソッドです。上で見てきたように...
#code(Python){{
def _insert(self, objs, fields, return_id=False, raw=...
"""
Inserts a new record for the given model. This pr...
the InsertQuery class and is how Model.save() is ...
"""
self._for_write = True
if using is None:
using = self.db
query = sql.InsertQuery(self.model)
query.insert_values(fields, objs, raw=raw)
return query.get_compiler(using=using).execute_sq...
_insert.alters_data = True
_insert.queryset_only = False
}}
Pythonってメソッドに属性を設定できるんですね。まあ普通使...
*django/db/models/sql [#lf888e03]
sqlモジュールに移りましょう。まずは__init__。
#code(Python){{
from django.db.models.sql.datastructures import EmptyResu...
from django.db.models.sql.query import * # NOQA
from django.db.models.sql.subqueries import * # NOQA
from django.db.models.sql.where import AND, OR
__all__ = ['Query', 'AND', 'OR', 'EmptyResultSet']
}}
InsertQueryはqueryモジュールに書かれていそうです。
・・・と思ったらsubqueriesの方に書いてありました。このsub...
**django.db.models.sql.query.Query.get_compiler [#se5beaab]
というわけでまずget_compilerから見てみましょう。
#code(Python){{
def get_compiler(self, using=None, connection=None):
if using:
connection = connections[using]
return connection.ops.compiler(self.compiler)(sel...
}}
出てきましたconnections。ここで前回も見てきた各DBMSに対応...
といっても、ops、sqlite3の方のDatabaseOperationsインスタ...
#code(Python){{
compiler_module = "django.db.models.sql.compiler"
def compiler(self, compiler_name):
"""
Returns the SQLCompiler class corresponding to th...
in the namespace corresponding to the `compiler_m...
on this backend.
"""
if self._cache is None:
self._cache = import_module(self.compiler_mod...
return getattr(self._cache, compiler_name)
}}
compiler_name、その元Queryインスタンスのcompiler、はInser...
**django.db.models.sql.compiler.SQLInsertCompiler.execute...
残りはexecute_sqlメソッドです。
#code(Python){{
def execute_sql(self, return_id=False):
self.return_id = return_id
with self.connection.cursor() as cursor:
for sql, params in self.as_sql():
cursor.execute(sql, params)
if self.connection.features.can_return_ids_fr...
return self.connection.ops.fetch_returned...
if self.connection.features.can_return_id_fro...
assert len(self.query.objs) == 1
return self.connection.ops.fetch_returned...
return self.connection.ops.last_insert_id(
cursor, self.query.get_meta().db_table, s...
)
}}
先に言っておくと、sqlite3の場合、can_return系のfeaturesは...
***as_sql [#xaf436f3]
as_sqlメソッドではDBMSのfeaturesも考慮してINSERT文が構築...
#code(Python){{
def as_sql(self):
# We don't need quote_name_unless_alias() here, s...
# going to be column names (so we can avoid the e...
qn = self.connection.ops.quote_name
opts = self.query.get_meta()
result = ['INSERT INTO %s' % qn(opts.db_table)]
has_fields = bool(self.query.fields)
fields = self.query.fields if has_fields else [op...
result.append('(%s)' % ', '.join(qn(f.column) for...
if has_fields:
value_rows = [
[self.prepare_value(field, self.pre_save_...
for obj in self.query.objs
]
else:
# 省略
# Currently the backends just accept values when ...
# queries and generate their own placeholders. Do...
# necessary and it should be possible to use plac...
# expressions in bulk inserts too.
can_bulk = (not self.return_id and self.connectio...
placeholder_rows, param_rows = self.assemble_as_s...
if can_bulk:
# 省略
else:
return [
(" ".join(result + ["VALUES (%s)" % ", "....
for p, vals in zip(placeholder_rows, para...
]
}}
pre_save_valメソッドはコメントにあるようにFieldクラスを呼...
#code(Python){{
def pre_save_val(self, field, obj):
"""
Get the given field's value off the given obj. pr...
things like auto_now on DateTimeField. Skip it if...
"""
if self.query.raw:
return getattr(obj, field.attname)
return field.pre_save(obj, add=True)
}}
prepare_value、assemble_as_sqlについて見ていきます。
***prepare_value [#k63fa07e]
prepare_valueメソッド。resolve_expressionは設定されていな...
#code(Python){{
def prepare_value(self, field, value):
"""
Prepare a value to be used in a query by resolvin...
expression and otherwise calling the field's get_...
"""
if hasattr(value, 'resolve_expression'):
# 省略
else:
value = field.get_db_prep_save(value, connect...
return value
}}
Fieldのget_db_prep_saveメソッド。は、get_db_prep_valueメ...
#code(Python){{
def get_db_prep_save(self, value, connection):
"""
Returns field's value prepared for saving into a ...
"""
return self.get_db_prep_value(value, connection=c...
prepared=False)
def get_db_prep_value(self, value, connection, prepar...
"""Returns field's value prepared for interacting...
backend.
Used by the default implementations of get_db_pre...
"""
if not prepared:
value = self.get_prep_value(value)
return value
}}
get_prep_valueメソッドはFieldクラスの各サブクラスでオーバ...
#code(Python){{
def get_prep_value(self, value):
value = super(DateTimeField, self).get_prep_value...
value = self.to_python(value)
if value is not None and settings.USE_TZ and time...
# 省略
return value
}}
***assemble_as_sql [#b25aee66]
SQLInsertCompilerクラスに戻って、assemble_as_sqlメソッド...
#code(Python){{
def assemble_as_sql(self, fields, value_rows):
"""
Take a sequence of N fields and a sequence of M r...
generate placeholder SQL and parameters for each ...
return a pair containing:
* a sequence of M rows of N SQL placeholder stri...
* a sequence of M rows of corresponding paramete...
Each placeholder string may contain any number of...
strings, and each parameter row will contain exac...
as the total number of '%s's in the corresponding...
"""
if not value_rows:
return [], []
# list of (sql, [params]) tuples for each object ...
# Shape: [n_objs][n_fields][2]
rows_of_fields_as_sql = (
(self.field_as_sql(field, v) for field, v in ...
for row in value_rows
)
# tuple like ([sqls], [[params]s]) for each objec...
# Shape: [n_objs][2][n_fields]
sql_and_param_pair_rows = (zip(*row) for row in r...
# Extract separate lists for placeholders and par...
# Each of these has shape [n_objs][n_fields]
placeholder_rows, param_rows = zip(*sql_and_param...
# Params for each field are still lists, and need...
param_rows = [[p for ps in row for p in ps] for r...
return placeholder_rows, param_rows
}}
見た感じ、何回も形式変換を行っています。とりあえず、field...
#code(Python){{
def field_as_sql(self, field, val):
"""
Take a field and a value intended to be saved on ...
return placeholder SQL and accompanying params. C...
expressions and fields with get_placeholder() def...
When field is None, the value is considered raw a...
placeholder, with no corresponding parameters ret...
"""
if field is None:
# 省略
elif hasattr(val, 'as_sql'):
# 省略
elif hasattr(field, 'get_placeholder'):
# 省略
else:
# Return the common case for the placeholder
sql, params = '%s', [val]
return sql, params
}}
というわけでまずrows_of_fields_as_sqlは、
('%s', [挿入する値])
のタプルのリスト(正確にはジェネレータ)になります。さら...
sql_and_param_pair_rowsは各オブジェクト(テーブルの一行)...
さらにzipを通すことで、placeholder_rowsとparam_rowsに分離...
placeholder_rows
(('%s', '%s'),)
param_rows
(([値], [値]),)
うーん、だんだんよくわからなくなってきた(上の形状は確認...
param_rows
[[値, 値]]
というリストに変換されます。このようにして返されたフィー...
*おわりに [#zcdd226f]
今回はモデル処理の手始めとしてモデルの保存について見てき...
また、SQLを組み立てるのにCompilerというオブジェクトが出て...
というわけで引き続き、「APIで遊んでみる」を見ていきましょ...
ページ名: