†ようやくマイグレーションが終わりDjangoのモデルを作成、保存できるようになりました。チュートリアルではマイグレーション後、「APIで遊んでみる」と題してモデルクラスが提供するAPIがどのようなものであるか紹介しています。q.save()少しずつ見ていきましょうということでとりあえずここまで。†というわけでModelクラスを見ていきます。modelsの__init__.pyはインポートしてるだけで実体はbase.pyに書かれています。†インスタンスを作成しているのでまずはもちろん__init__メソッドを確認します。self).__init__()何をしているかというと、メタ情報として持っている各フィールドについてキーワード引数から値を取得しインスタンスの属性として設定を行っています。普通のクラスでやるような初期化をメタ情報から名前を取ってきて設定している、ということになります。†というわけでモデルのインスタンス化ができたので次は保存を行うsaveメソッドです。update_fields=update_fields)普通に使っている分には結局この2行だけになります。usingは、routerは前回も出てきたデータベース振り分けの仕組みですが、振り分けしてなければ"default"のDBを使うということになってsave_baseメソッドに続きます。False_save_tableに続きます。tableという名前が出てきたことからそろそろデータベースとやり取りする層に降りてくると思われます。重要そうなのでセクションを変えて、_save_tableメソッドです。updated_get_pk_valメソッドを呼び出してpk、主キーを取得しています。新規レコードなので当然結果はNoneです。次に、フィールドクラスのget_pk_value_on_saveメソッドが呼ばれていますが通常はNoneが返される、というわけで_do_insertが呼ばれてINSERTが行われます(上のコードでは省略していますが、pkの値があるならUPDATEが行われるようになっています)_do_insertメソッドはシンプル、というかmanagerへの移譲が行われているだけです。†ところで、manager、より正確には_base_managerとは何者なのでしょうか。メタクラスのModelBaseを見てみましょう。cls._meta.base_manager_meta、optionsモジュールのOptionsクラスに続く。base_managerプロパティはいろいろやっていますが関係ないとこ省くと以下のようになります。managerというわけで、managerモジュールのManagerクラスのインスタンスが返されます。ところで、managerという単語は前にも出てきた気がします。manager)base_managerプロパティを見たとき、「ややこしい処理には行かないはずだからこの最後に書いてあるのだろ」と思ったのですが、「いや待て、Managerインスタンスってもういるはずだよな」とこちらの処理を思い出しadd_to_class以降に進んだのですが、結局、INSERTの処理をする時はこの'objects'は使われないということがわかりました。Managerが2ついるのはどうにも気持ち悪いですが事実そうなっているようです。†さて、Managerクラスです。class_dict)_get_queryset_methodsでQuerySetからメソッドを取得し、新しいクラスを作成しています。なんて初めて見た(笑)get_querysetメソッド。なんで毎回別のオブジェクト作ってるんだろ、と思いましたが、確かにINSERTはともかく他のだと検索情報を個別に記録しないといけませんね。†さて、話を戻して、_insertメソッドです。上で見てきたように各種のメソッドはManagerに直接書かれているわけではなくqueryモジュールのQuerySetクラスに書かれています。FalsePythonってメソッドに属性を設定できるんですね。普通使うことはないと思いますが。†sqlモジュールに移りましょう。'EmptyResultSet']InsertQueryはqueryモジュールに書かれていそうです。・・・と思ったらsubqueriesの方に書いてありました。このsubというのは「SQLの副問い合わせ」という意味じゃなくて「Queryクラスのサブクラス」という意味のようですね。insert_valuesではSQLの構築は行われずインスタンスへの設定だけが行われるようです。†というわけでまずget_compilerから見てみましょう。using)出てきましたconnections。ここで前回も見てきた各DBMSに対応したDatabaseWrapperに処理が移ります。いっても、ops、sqlite3の方のDatabaseOperationsインスタンスにはcompilerメソッドは定義されておらず、基底クラスのBaseDatabaseOperationsに定義されています。compiler_name)compiler_name、その元Queryインスタンスのcompiler、はInsertQueryの場合、SQLInsertCompilerになります。django.db.models.sql.compiler.SQLInsertCompilerが返されることになります。残りはexecute_sqlメソッドです。)先に言っておくと、sqlite3の場合、can_return系のfeaturesはTrueにはならず最後のlast_insert_idの方法でINSERTされたIDが返されるようです。†as_sqlメソッドではDBMSのfeaturesも考慮してINSERT文が構築されます。]pre_save_valメソッドはコメントにあるようにFieldクラスを呼び出して時間を「保存した時」にするなどの処理を行っていますが通常は単にgetattrで値を取得しているだけです。add=True)prepare_value、assemble_as_sqlについて見ていきます。†prepare_valueメソッド。resolve_expressionは設定されていないはずなのでFieldクラスのget_db_prep_saveメソッドが実行されます。valueFieldのget_db_prep_saveメソッド。get_db_prep_valueメソッドを呼んでるだけです。get_db_prep_valueではget_prep_valueメソッドを呼び出しています。valueget_prep_valueメソッドはFieldクラスの各サブクラスでオーバーライドされているようです。以下はDateTimeFieldのget_prep_value。to_pythonメソッドは省略しますがvalueをdatetimeオブジェクトにする処理をしています。†SQLInsertCompilerクラスに戻って、assemble_as_sqlメソッドです。param_rows見た感じ、何回も形式変換を行っています。field_as_sqlを見てみましょう。[挿入する値])のタプルのリスト(正確にはジェネレータ)になります。それがオブジェクト分(今回は1個)のジェネレータになっています。値)タプルのzipオブジェクトのジェネレータです。zipを通すことで、placeholder_rowsとparam_rowsに分離しています。それぞれ以下のような形状をしています。[値]),)うーん、だんだんよくわからなくなってきた(上の形状は確認として打ってみた結果です)。最後に、param_rowsで値が1要素のリストになっているのをflattenしています。値]]というリストに変換されます。このようにして返されたフィールドと値のうち、フィールドはSQLの構築に利用、値は最終的にカーソルオブジェクトに渡されてデータベースに送られることになります。今回はモデル処理の手始めとしてモデルの保存について見てきました。Managerオブジェクトが登場し、QuerySetと絡んで複雑な処理が行われていました。INSERTの場合は個別のQuerySetは要らないので冗長に感じますが今後なぜこのような構造が必要なのかが出てくると思われます。SQLを組み立てるのにCompilerというオブジェクトが出てきました。コンパイラと言ってもSQLを受け取って構文解析するコンパイラではなく、SQLを組み立てるコンパイラです。これもINSERTだと冗長な感じでしたが複雑な検索処理が設定された場合に力を発揮するのでしょう。いうわけで引き続き、「APIで遊んでみる」を見ていきましょう。