Djangoの実装と設計|Pythonで学ぶシステム設計 #3

f:id:lib-arts:20190105175431p:plain

#2ではscikit-learnについて取り扱いました。
色々と他のライブラリを確認する方が面白いと思いますので、#3ではDjangoについて取り扱っていきます。

以下目次になります。

1. Pythonの実装の読み方の補足
2. MTVフレームワークについて
3. Model周りの実装
4. manage.py周りの実装
5. 考察


1. Pythonの実装の読み方の補足
#2の記事で言及しましたので、こちらをご確認ください。

scikit-learnの実装と設計|Pythonで学ぶシステム設計 #2 - lib-arts’s diary


2. MTVフレームワークについて

Djangoのソースを追うにあたって、MTVフレームワークの考え方は先に押さえておくべきなので補足します。

Webの開発にあたって、普及当初はHTMLなどを中心とした静的なページが中心でしたが、ユーザーのアクセス(request)に対して挙動を変えたいなど様々なニーズが生じたため、スクリプト言語などをベースに処理を記述してレスポンス(response)を返すという形に色々と発展を遂げてきました。当初はCGI(Common Gateway Interface)を用いた簡単な処理が多かったのですが、段々と高度な処理が求められるようになっていく一方で、Webアプリを開発する上で共通となる部分はそこまで大きく変わらないのでそれをノウハウ化しようという動きが盛んになっていきました。その中でノウハウ化したものをフレームワークとして開発を容易にしていこうということで様々なオープンソースのWebフレームワークが開発されてきました。Webのフレームワークは大きく分けるとDjangoRailsのような機能がたくさん入ったフルスタックのフレームワークと、SinatraやFlaskなどの軽量のフレームワークに分かれます。今回読んでいくDjangoフルスタックと言われる機能が多数入ったフレームワークとなります。

フルスタックのフレームワークについて議論する際の設計としてはMVC(Model View Controller)が有名です。それぞれModelがデータベース関連の実装、Viewが画面表示関連(HTMLやCSSなどのファイルの作成)、Controllerがその処理のつなぎを担当しています。細かい点は違いますが、#1で取り扱った『解決策は問題に依存する』という言葉の通り、Webの開発という課題を考えた際にMVCのような考え方が自然と設計思想として必要になってくるのだと思われます。

Djangoを考える際にもベースはMVCを起点に考えると良いのですが、非常に紛らわしい点があり、DjangoのViewはMVCにおけるControllerの役割を果たしていることです。そのため、DjangoMVCと呼ぶよりMTV(Model Template View)フレームワークと呼ぶ方が適切だと思われます(MVCもMTVも本質的な原理としてはさほど変わりないですが、流派みたいなものとして捉えるのが良いかと思います)。ここでModelはMVC同様データベース周り、TemplateはHTMLやCSSなどの画面表示周り、ViewはControllerの役割です。

以上がMTVフレームワークの概要になります。


3. Model周りの実装

DjangoのModel周りの実装を考えるにあたって知っておきたいのがORM(Object-Relational Mapping)です。ORMはデータベース(MySQLPostgreSQLなど)とオブジェクト指向(PythonRubyなど)の間の非互換なデータを変換するプログラミング技法のことです。抽象的で分かりづらいかもしれないのでざっくり表現すると、データベースをPythonから操作するということです。そのため、SQL系の機能をPythonで置き換えるという視点で読むと良いです。

まずはウォーミングアップがてらFieldクラスの実装を見ていければと思います。

from django.db import models

class Table1(models.Model):
    ...
    column1 = models.CharField(...)

上記が実装例となりますが、ここでmodels.CharFieldでTable1におけるcolumn1のフィールド定義を行なっています。こちらの元実装について確認してみましょう。元実装に関しては、これまでのやり方と同様にPJ_root/django/db/models/__init__.pyを見るとここにmodels.ModelクラスやFieldクラスの読み込みを行なっているはずです。
https://github.com/django/django/blob/master/django/db/models/__init__.py
実際にGitHub上の実装を見てみると__init__.pyは上記のファイルにあたります。

from django.db.models.base import DEFERRED, Model

ここでmodels.Modelは上記より、同一ディレクトリ内のbase.pyで実装されていることが分かります。

from django.db.models.fields import *

また、Fieldクラスは分かりにくいですが、上記から読み込んでいます。すなわち下記のファイル内にCharFieldなどの実装が見つかります。
https://github.com/django/django/blob/master/django/db/models/fields/__init__.py


また、Djangoでよく使われるモデルの例としてUserモデルについても簡単に確認してみました。

django.contrib.auth.models.User
-> django.contrib.auth.models.AbstractUser
-> django.contrib.auth.base_user.AbstractBaseUser

のような継承関係になっているので、親クラスのAbstractBaseUserの実装を見た所下記のようにmodels.Modelを継承していることが分かりました。

class AbstractBaseUser(models.Model):
     ....
https://github.com/django/django/blob/master/django/contrib/auth/base_user.py

このことから、UserクラスはModelクラスを拡張してユーザー管理の機能を足したものだと解釈すると良いことが分かります。


FieldやUserの実装を見ることでDjangoの実装の雰囲気を掴むことができたのではないかと思います。

 

4. manage.py周りの実装
また、manage.pyでサブコマンドによって処理を切り分けていたのが気になったのでこちらも調べてみました。
こちらは長くなるので結論だけまとめると、django.core.management.base.commands以下に諸々のコマンドの処理をまとめており、これを外部からimportして処理の挙動を変えていました。
考え方としてはstrategyパターンに近いのではと思われます。

 

5. 考察
サブコマンドの処理にあたってstrategyパターンのようなパターンを用いているのは参考になりました。様々な処理を並列で並べたい際はstrategyパターン的な実装方法が良いのではないかと見ていて感じました。