Django ベストプラクティス

Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported

このドキュメントは Django Best Practices の日本語版です。 原著者は Brian Rosner , Eric Holscher です。

(翻訳版 github リポジトリ)


これは Django ウェブフレームワーク の開発とデプロイにおけるベストプラクティスとして、随時更新されるドキュメントです。これらは Django で作業する上での、 正しい やり方、 唯一の やり方を指南するものではありません。その代わり、我々が何年にも渡りこのフレームワークを使い磨いてきたベストプラクティスではあります。

そしてこれは、 Brian RosnerEric Holscher によって、再利用可能な Django アプリケーションを書き、保守するためのベストプラクティスにとして始められた、偉大な django-reusable-app-docs project のフォークです。

注釈

このドキュメントのソースコードは GitHub 上の django-best-practices (オリジナル) にあり、 Sphinx を使うことでいくつものフォーマットでビルド可能です。

フィードバック

問題を見つけたら?納得いかないことがあったら?触れられていないトピックについて見たかったら? 我々はあなたからのフィードバックを歓迎します! 次の方法でお願いします。 メール (オリジナル版:英語) または Issue の提出 (オリジナル版:英語)。 (日本語版 Issue はこちら)

目次

コーディングスタイル

一般的に、コードはクリーンで簡潔で、読みやすくあるべきです。 The Zen of Python (PEP 20) は Python のベスト・コーディング・プラクティスのための優れた序章です。

Django プロジェクト

core において、Django プロジェクトはセッティングファイル以外を必要としません。このプラクティスにおいては、大体のプロジェクトが次のような事項から成っています。

セッティング

settings モジュールは Django プロジェクトにおいて必須です。一般的に、 settings.py という名前で、あなたの作ったプロジェクトのルートに存在します。

複数環境でのセッティングの扱い

Django での startproject コマンドは settings.py ファイルを 1 つ作成します。もしあなたが Django に触れて間もない場合、こつこつ学習する間はずっとこのファイルと一緒です。本番サイトのデプロイを始めたり、複数人で開発作業をする際に、複数のセッティングファイルをメンテナンスすることの恩恵を実感することでしょう。例えば、本番ではなく、ローカル環境で、よくある DEBUG 付きの実行をしたい場合などです。

複数セッティングを扱うにはいくつもの方法があります。次に挙げる要件に合わせて、好きな方法を選んでください。

  • 全ての重要な セッティングファイルは バージョン管理されている 。 もし本番サイトでセッティングが変わったときは、あなたは誰が、いつ、その変更を加えたのかを知りたいと思うでしょう。
  • 全てのセッティングが common base から継承されている 。 もしあなたが django-debug-toolbarINSTALLED_APPS に追加したいときは、全ての INSTALLED_APPS を再定義することなしに、それを行うべきです。

もしそういったことを考慮したくない場合は、シンプルに我々の Django プロジェクトテンプレートを、新しくプロジェクトを始めるときに使ってください。そのプロジェクトテンプレートでは、一歩踏み出すための複数プロジェクトをサポートする準備が整っています

django-admin.py startproject --template=https://github.com/lincolnloop/django-layout/tarball/master -e py,rst,example,gitignore my_project_name

参考

Django’s Split Settings Wiki
複数セッティングを扱う例があります

ファイルパスの扱い

あなたのセッティングの一つの機能は、 Django に静的メディアファイルやテンプレートを探す場所を知らせることです。おおよその場合は、それらは既にあなたのプロジェクトの中に存在して使用されています。もしそうであれば、あなたのために Python に絶対パスを生成させてください。こうすることで、異なる環境間でも、あなたのプロジェクトをポータブルに利用することができます。

import os
DIRNAME = os.path.dirname(__file__)
# ...
STATIC_ROOT = os.path.join(DIRNAME, 'static')

URLconf

デフォルトでは、あなたのプロジェクトのルートに URLconf を urls.py という名前で見つけることができるでしょう。これはリクエストがあなたのプロジェクトでどのようにルーティングされるかを定義しています。

シンプルに保つ

あなたのプロジェクトの URLconf は、可能であればいつでも、あなたのアプリケーションから URLconf 群をシンプルにインクルードすべきです。そうしておけば、あなたが作ったアプリケーションロジックやプロジェクトを、ポインターとしてシンプルに提供できます。

参考

Django URL dispatcher documentation
URLconf 群をインクルードする例

複数環境のための URLconf 群の扱い

あなたのセッティングモジュールのように、結局は、異なる URLconf 群にまたがって実行したい場面に出くわすことでしょう。あたなは多分、 admin をローカルで使いたいでしょうが、一度デプロイするだけでは終わらないでしょう。Django はこのための簡易な手段を既に提供しており、 ROOT_URLCONF セッティングを一緒に使うことで実現できます。

これは基本的に、 複数セッティング によるものと同じシナリオです。同じことをする、次のような解決法もあります:

myproject
  ...
  settings/
        __init__.py
        base.py         <-- 全ての環境で共有される
        def.py
        production.py
  urls/
    __init__.py
        base.py                 <-- 全ての環境で共有される
    dev.py
    production.py
  ...

WSGI ファイル

WSGI ファイルは、あなたのプロジェクトを web で公開するために必要な情報を WSGI サーバーに教えます。Django のデフォルトの wsgi.py はおおよそのアプリケーションにとって十分な内容になっています。

ローカルアプリケーション

ローカルアプリケーションは、あなたのプロジェクトにドメイン固有な Django アプリケーションです。それらはおおよそ、プロジェクトモジュールの中に存在しており、あなたのプロジェクトに密に関係しており、その外側ではほとんど利用されないでしょう。

ローカル vs. サードパーティ

数百 [1] のオープンソース Django アプリケーションが利用可能です。車輪の再発明をする前に、既に他の誰かが、あなたが解決しようとしてる問題を解決していないか、 Google または Django Packages で検索し、確認しましょう。 もし解決してくれそうなものを見つけたら、 あなたのプロジェクトコードに書き入れる のではなく 、代わりに、あなたの環境の pip requirements に追加しましょう。

[1]http://djangopackages.com/categories/apps/

名前空間

ローカルアプリケーションがどのようにあなたのプロジェクトに取り込まれるべきかは、 Django コミュニティ [2] で継続的に議論されています。幸い Django 1.4 のリリースで、デフォルトの manage.py は今後はもう PYTHONPATH [3] を変更しないので、問題が少なくなりました。

Lincoln Loop においては、我々はプロジェクトの名前空間内にプロジェクトアプリケーションを入れます。これにより、グローバルの名前空間の汚染を防ぎ、名前が衝突する潜在性を回避します。

[2]django-developers メーリングリストでの、チュートリアルにおける、プロジェクトの名前空間についての議論
[3]Django 1.4 での manage.py の変更点

テンプレート

場所

テンプレートはおおよそ、2 つの場所、アプリケーションか、プロジェクトのルート階層のうち、どちらかに存在します。我々のおすすめは、あなたが複数のプロジェクトを自分のアプリケーションに含めない限り(または “再利用可能な” オープンソースのアプリケーションとして開発しない限り)、全てのあなたのテンプレートを、プロジェクトのテンプレートディレクトリに保持することです。その場合、アプリケーション中のサンプルテンプレートのセットを含めて公開するのに有用で、その状態のままで動かすこともできますし、他の開発者へのサンプルとして提供することもできます。

名前付け

Django のジェネリックビューはテンプレートに名前付けするための優れたパターンを提供します。Django に既にあるデザインパターンに従うことは、次の 2 つの理由で有用です。

  1. それらは、よく考え抜かれていますし、よくテストもされています
  2. あなたの Django コードを使う新しい開発者にとって、コードを読みやすくします。

大体のジェネリックビューテンプレートは次の形式で名前付けされます

[application]/[model]_[function].html

例えば、自分の連絡帳 (address_book application) にある全ての連絡先 (Contact model) を表示するリストのテンプレートを作成する際、私は次のテンプレートを使います

address_book/contact_list.html

同じようなものとして、連絡先の詳細ビューでは次のものを使います

address_book/contact_detail.html

とは言え、毎回、あなたが作ったテンプレートが、一つ一つのモデルにぴったりと対応付け出来るわけではないでしょう。それらのケースでは、名前付けにおいて、あなた自身で頑張って行かなければいけません。それでも、そのテンプレートは、アプリケーションと同じ名前のディレクトリの中にはあるはずです。

埋め込みタグや、部分テンプレートをレンダリングするような、その他多くの機能を使う場合は、アプリケーションテンプレートディレクトリ内の includes ディレクトリにそれらを保持するようにしてください。例として、もし私が、連絡帳アプリケーションの中の連絡用フォームの埋め込みタグを設定しているとしたら、次のようにテンプレートを作ったことでしょう。

address_book/includes/contact_form.html

テンプレートファイルは必ず html の拡張子を付けなければいけないという制限は (もはや) ありません。もし何か他のファイル形式 (プレーンテキスト, JSON, XML, その他) をレンダリングする際、あなたのテンプレートファイルの拡張子は、生成するコンテンツの拡張子と一致させておけば良いでしょう。

静的メディアファイル

CSS, 画像, JavaScript, Flash などの静的メディア (あなたのサイトに必要な、動的じゃない全てのコンテンツ) は、 ユーザー側で生成されるコンテンツ、それと、あなたのサイトをレンダリングするのに必要なメディアファイル、の 2 つに分けられます。このベストプラクティスは、それらを、あなたのアプリケーションの中にあるもの、それと、バージョン管理システムの中にある あなたの 静的メディアファイルのこととして書いています。確かに、自分が抱えるユーザーがアップロードしたものが、一箇所に固るのは我々も望みません。というわけで、我々はいつも django.contrib.staticfiles [4] を使います。

その他の巧妙な機能に加えて、 staticfiles は、静的ファイルがローカルマシン上にあったり、本番システム上のローカルじゃないストレージ内にあったりするどちらの場合でも、静的ファイルを適切に設置する static テンプレートタグ [5] を提供します。この仕組みは、ユーザー生成コンテンツを管理する MEDIA_URLMEDIA_ROOT を残します。

[4]https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/
[5]https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:templatetag-staticfiles-static

Django アプリケーション

Django プロジェクトは大体の場合、 INSTALLED_APPS に宣言された数々のアプリケーションから成ります。Django アプリケーションは、小さくあること、モジュール (組立部品) であることにフォーカスした、 Unix の哲学 “一つのことを、うまくやれ (Do one thing and do it well.) ” [1] に従うべきであり、Django の “ルースカップリング” 設計哲学 [2] を反映すべきです。

最初の DjangoCon での James Bennett による 再利用可能なアプリトーク は、 Django での良いアプリケーションの構築を主題にした、とても良く出来た入門です。

[1]http://en.wikipedia.org/wiki/Unix_philosophy#McIlroy:_A_Quarter_Century_of_Unix
[2]https://docs.djangoproject.com/en/dev/misc/design-philosophies/#loose-coupling

コード構成

Django アプリケーションにただ 1 つ必要なものは models.py ファイルで提供されるものです。ただ、このプラクティスでは、 Django アプリケーションはいくつもの種類が違うファイルから成り立ちます。あなたがアプリケーションをビルドする場合、共通のファイル命名規則に従ってください。 manage.py startapp <foo> を実行することで Django が提供するフレームワークで開発を始め、必要に応じて増築してください。

  • __init__.py
  • admin.py
  • context_processors.py
  • feeds.py
  • forms.py
  • managers.py
  • middleware.py
  • models.py
  • receivers.py
  • signals.py
  • templates/app_name/
  • templatetags/
    • __init__.py
    • app_name.py
  • tests.py または tests/
  • urls.py
  • views.py

これらのファイルの中で動かすものは、一つ一つが一見して、それが何なのかすぐに分かるものであるべきです. さぁ、始めましょう。(Let’s dive into some of the meatier ones though.)

モデル

スタイル

モデルのコードは Django の 決定済み規約 に従いましょう。

モデルを太らせましょう

MVC-スタイルプログラミングでの共通パターンは、太ったモデルと痩せたコントローラーを構築するものです。Django ではこれを次のように読み替えます。自身に関係する沢山の小さなメソッドを持ったモデルの構築、モデルに付随するメソッドを使い、自身では可能な限りミニマルなロジックを保つビューです。このアプローチには多くの利点があります。

  1. DRY: 複数のビューに同じロジックを繰り返し書くより、モデル内に一度だけ定義。
  2. テスト可能: モデルのロジックを小さなメソッドに切り出していくことで、あなたのコードがユニットテストしやすくなる。
  3. 読みやすさ: あなたのメソッドに分かりやすい名前を付けることで、ひどいロジックを、読みやすくて分かりやすいものにまとめられる。

Django における太ったモデルの良い例としては、 |django.contrib.auth.models.User の内容|_ を参照してください。

マネージャー

モデルに似て、共通のロジックをマネージャーの中にまとめるのも良いやり方です。より具体的に言うと、多分、あなたは、クエリセットとして使えるようなチェーンメソッドが欲しくなるでしょう。これは私がいつも忘れるボイラープレートを含みます。ここに (ほぼ) コピー・アンド・ペーストした例を記載します

import datetime
from django.db import models
from django.db.models.query import QuerySet

class PostQuerySet(QuerySet):
    def live(self):
        """公開の準備が出来てない投稿をフィルタリングする"""
        now = datetime.datetime.now()
        return self.filter(date_published__lte=now, status="published")

class PostManager(models.Manager):
    def get_query_set(self):
        return PostQuerySet(self.model)
    def __getattr__(self, attr, *args):
        # 詳しくは https://code.djangoproject.com/ticket/15062 を参照
        if attr.startswith("_"):
            raise AttributeError
        return getattr(self.get_query_set(), attr, *args)

class Post(models.Model):
    # フィールド定義
    objects = PostManager()

このコードにより、 Post.objects.live() とクエリセットを使ってメソッドチェーン化した Post.objects.filter(category="tech").live() の両方のマネージャーから、新たなメソッド live を直接コールすることができます。コードを書く際には an open bug を見ると手間を減らせると思います。

デプロイ

コンテンツ:

プロジェクト・ブートストラッピング

ファイルシステムのレイアウト

注釈

このドキュメントは Unix-スタイル にかなり偏っているので、他のオペレーティングシステム上で同じ事をやるには少し余計に頑張りが必要とするかも知れません。

Virtualenv は Python projects に必須です。 これは異なる Python 環境を独立させる方法を提供します。 我々は慣例的に本番サイトを /opt/webapps/<site_name> で、開発用サイトを ~/webapps/<site_name> でホストします。 それぞれのプロジェクトは個別に、それぞれの virtualenv を取得し、それはプロジェクトに関連付けられた全てのソースファイルの含んだディレクトリとしても提供されます。 我々は virtualenv に必要なパッケージを追加するのに pip を使います。

ブートストラップ・プロセスは次のようになります:

cd /opt/webapps
virtualenv mysite.com
cd mysite.com
source bin/activate
pip install -r path/to/requirements.txt

ちなみに

より快適な作業のために、 virtualenvwrapper を使うのも良いでしょう。これは複数の virtualenv での作業に便利なヘルパーを提供しています。

パッケージング

成功する開発の鍵の 1 つは、あなたが開発するソフトウェアを、あなたがデプロイするソフトウェアとできるだけ近くにするべきである、ということです。 Pip は複数のマシンをまたいで Python のプロジェクトを堅実にデプロイできる、シンプルで何度も繰り返せる方法を提供します。 サードパーティ・ライブラリを必須とする全てのアプリケーションは requirements.txt と呼ばれる pip requirements file を含むべきです。 プロジェクトはアプリケーションに必須なファイル、さらに必要に応じて追加で必須になるファイルを集合させておくべきです。

何をあなたの必須ファイルに含めるか

つまり、全部です。あなたのオペレーティングシステムが Python パッケージ群を提供するであろう間は、最近は pip によりおよそ全てがクリーンにインストールされます。 あなたの virtualenv に全てがインストールされることで、環境が独立されて、システムパッケージにバージョンのコンフリクトが起きることを防ぎます。

警告

依存関係を押さえておいてください! Pip は VCS からのインストールを簡単にします。そうすれば、PyPI 上で見つかるどのバージョンも取ってくるだけで済みます。またこうすることで、不測の事態を起こすような異なるライブラリの異なるバージョンを含んだデプロイが簡単になります。 VCS でのチェックアウトのために、 PyPI ライブラリ群のバージョン、または、コミット/タグをしっかりと特定しておいてください。 例: django==1.4.1 または -e git+https://github.com/toastdriven/django-tastypie.git@v0.9.9#egg=django-tastypie

サーバー

注釈

デプロイの考え方 (アーキテクチャー) は何が必要かとサイトのトラフィックに広く依存します。下記のセットアップについての記述は、最低限の設定であり、大体のインスタンスではうまく動きます。

我々は、フロントエンドプロキシ Nginx の裏で gunicorn または uWSGI を経由して、PostgreSQL データベースバックエンドと一緒に Ubuntu Linux 上で Django を動かします。便宜上、ここでは Gunicorn/Nginx のみ取り扱います。

Nginx

Nginx はそのスピード、安定性、少ないリソース・フットプリントから優秀なフロントエンドサーバーを形成します。サイトのためのよくある Nginx の設定は次のようになります:

# Gunicorn サーバー
upstream django {
  server         domain.com:9000;
}

# www サブドメイン上のの全てのリクエストをルートドメインにリダイレクト
server {
  listen      80;
  server_name www.domain.com;
  rewrite ^/(.*) http://domain.com/$1 permanent;
}

# 静的ファイルを送出し、その他のリクエストは Apache へリダイレクト
server {
  listen       80;
  server_name  domain.com;
  root        /var/www/domain.com/;
  access_log  /var/log/nginx/domain.com.access.log;
  error_log  /var/log/nginx/domain.com.error.log;
  
  # 外部からのリクエストで /var/www/domain/ にファイルが存在するかチェック
  # もし存在しなければ、Gunicorn/Django へ中継 (proxy) する
  try_files $uri @django;
  
  # Django のリクエストの名前付きロケーションのセットアップと、中継 (proxy) の処理の詳細
  location @django {
    proxy_pass         http://django;
    proxy_redirect     off;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  }
}

それは何をしますか ?

最初のブロックは Nginx に我々の Django サイトをホスティングするサーバーをどこに探せば良いか知らせます。二番目のブロックは www.domain.com に来るどんなリクエストも domain.com にリダイレクトさせます。従って、各リソースは、canonical URL をただ1つ持つことになります。最後のセクションは、全ての処理についてのものです。この記述は、 /var/www/domain 内のファイルにリクエストがマッチするかを Nginx にチェックさせます。もしマッチすれば、ファイルが送出され、もしマッチしなければ、 Django サイトへリクエストを中継します。

SSL

フロントエンドサーバーを走らせる、その他の恩恵としては、SSL のターミネーションです。 SSL アクセスと 非 SSL アクセスで走らせる Django インスタンスを 2 つ持つよりも、 localhost 上でリッスンしている単一の非 SSL の WSGI インスタンスに、全てのリクエストをリダイレクトして返すゲートキーパーとして振る舞う Nginx を選択します。ここにどのようなものになるかを記載します:

server {
  listen       67.207.128.83:443; # あなたの使う ip アドレスに置き換えてください
  server_name  domain.com;
  root        /var/www/domain.com/;
  access_log  /var/log/nginx/domain.com.access.log;

  ssl on;
  ssl_certificate /etc/nginx/ssl/certs/domain.com.crt;
  ssl_certificate_key /etc/nginx/ssl/private/domain.com.key;
  ssl_prefer_server_ciphers       on;
  
  # 外部からのリクエストで /var/www/domain/ にファイルが存在するかチェック
  # もし存在しなければ、Gunicorn/Django へ中継 (proxy) する
  try_files $uri @django;
  
  # Django のリクエストの名前付きロケーションのセットアップと、中継 (proxy) の処理の詳細
  location @django {
    proxy_pass         http://django;
    proxy_redirect     off;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Protocol ssl;
  }
  	    
}

非 SSL 設定ファイルの末尾に、このコードを入れることもできます。

Gunicorn

Gunicorn は軽量 WSGI サーバーです。これは高トラフィックスサイト用にも小さなデプロイからスケールすることができます。 pip install gunicorn からインストールできます。 Nginx は HTTP(S) でリッスンするので、 Gunicorn には違うポートをバインドする必要があるでしょう。 その状態でなら、あなたは localhost への応答だけを教えてあげるだけです。単純な gunicorn プロセスは次のようになります:

$ gunicorn --workers=4 --bind=127.0.0.1:9000 my_project.wsgi:application

これは gunicorn のプロセスを、 http://127.0.0.1:9000 でリッスンする 4 つの worker で生成します。 もしあなたのプロジェクトにまだ wsgi.py ファイルがなければ、そのファイルを加えたいと思うでしょう。 the Django WSGI docs または django-layout を例として参照してください。

プロセス・マネジメント

あなたは gunicorn が常に走っていて、サーバーの再起動時にも自動的に立ち上がることを確かなものにしたいでしょう。 もし Ubuntu にデプロイするなら、 upstart はおそらく、その最初として最も簡単な方法です。ここにサンプルの設定を記載します:

# ログは /var/log/upstart/my_project.log へ

description "my_project"
start on startup
stop on shutdown

respawn

# virtualenv パスから起動する
exec /opt/webapps/my_project/bin/gunicorn  -w 4 -b 127.0.0.1:9000 my_project.wsgi:application
setuid www-data

これをファイルとして /etc/init/gunicorn.conf に保存し sudo start gunicorn を実行してください。 トラブルシューティングのために、 あなたのログは /var/log/upstart/gunicorn.log で見れるようになっていると思います。

注釈

Supervisor は、 Python のみで書かれていて、もしあなたが upstart にアクセスできない場合の選択肢です。