Django ドキュメント 目次

revision-up-to:11321 (1.1)

Django ドキュメント

revision-up-to:17812 (1.4)

Django を使いこなすための全てがここにあります。

和訳について

このドキュメントは、 Django の標準ドキュメント の和訳です。和訳は日 本 Django ユーザ会の有志の手でメンテナンスされています。和訳に関する問 い合わせは、 Django プロジェクトのトラッカではなく 、 django-ja メ ーリングリストにお寄せください。

助けを求める

困り事ですか?手を貸しましょう!

最初のステップ

テンプレート層

オープンソースプロジェクトとしての Django

さあ始めましょう

revision-up-to:17812 (1.4)

Django は初めてですか? Web 開発全般も?なら、ここに来て正解ですね。ここに あるドキュメントを読んで、行動に移してみましょう。

Django の概要

revision-up-to:17812 (1.4)

Django は変転の激しいニュースルーム環境で開発された経緯から、よくある Web 開発タスクを迅速かつ簡単化するように設計されました。ここでは Django による データベース中心の Web アプリケーション開発をざっと見てみましょう。

このドキュメントの目的は、 Django の技術的な仕様について述べ、どのように動 作するかを理解してもらうことにあり、チュートリアルやリファレンス用ではあり ません。(とはいえ、チュートリアルもリファレンスも別に用意していますよ!) プロジェクトを作成する準備ができたら、 チュートリアルをはじめる か、 より詳細なドキュメントに読み進んで みてください。

モデルの設計

Django はデータベースなしでも使えます。とはいえ、 Django にはオブジェクト- リレーショナルマッパが付属していて、 Python コードでデータベースのレイアウ ト記述できるようになっています。

Django の データモデルシンタクス はモデルを表現 するための色々な方法を提供しています。この API には、 2 年間にわたって様々 なデータベーススキーマの問題を解決してきた実績があります。 mysite/news/models.py ファイル内に保存されるような、簡単な例を示しまし ょう:

class Reporter(models.Model):
    full_name = models.CharField(max_length=70)

    def __unicode__(self):
        return self.full_name

class Article(models.Model):
    pub_date = models.DateTimeField()
    headline = models.CharField(max_length=200)
    content = models.TextField()
    reporter = models.ForeignKey(Reporter)

    def __unicode__(self):
        return self.headline

モデルのインストール

次に、Django コマンドラインユーティリティを実行し、データベース上にテーブル を自動的に生成します:

manage.py syncdb

syncdb コマンドは利用可能な全てのモデルを探し、まだ作成されてい ないテーブルがあれば作成します。

自動生成される API で楽しむ

これだけで、制約のない充実した Python API を使っ て自分のデータにアクセスできます。API はオンザフライで生成され、コードを作 成する必要はありません:

.. # Import the models we created from our "news" app

# "news" アプリで作成したモデルを import します。
>>> from news.models import Reporter, Article

.. # No reporters are in the system yet.

# まだシステム上に Reporter はひとつもありません。
>>> Reporter.objects.all()
[]

.. # Create a new Reporter.

# 新しい Reporter を作成します。
>>> r = Reporter(full_name='John Smith')

.. # Save the object into the database. You have to call save() explicitly.

# オブジェクトをデータベースに保存します。
# 明示的に save() を呼ばねばなりません。
>>> r.save()

.. # Now it has an ID.

# オブジェクトに ID が割り当てられました。
>>> r.id
1

.. # Now the new reporter is in the database.

# 作成した Reporter はデータベース上にあります。
>>> Reporter.objects.all()
[<Reporter: John Smith>]

.. # Fields are represented as attributes on the Python object.

# 各フィールドは Python オブジェクトの属性として表現されています。
>>> r.full_name
'John Smith'

.. # Django provides a rich database lookup API.

# Django は充実したデータベース検索 API を提供しています。
>>> Reporter.objects.get(id=1)
<Reporter: John Smith>
>>> Reporter.objects.get(full_name__startswith='John')
<Reporter: John Smith>
>>> Reporter.objects.get(full_name__contains='mith')
John Smith
>>> Reporter.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Reporter matching query does not exist.

.. # Create an article.

# Article を作成します。
>>> from datetime import datetime
>>> a = Article(pub_date=datetime.now(), headline='Django is cool',
...     content='Yeah.', reporter_id=1)
>>> a.save()

.. # Now the article is in the database.

# これで Article はデータベースに入りました。
>>> Article.objects.all()
[<Article: Django is cool>]

.. # Article objects get API access to related Reporter objects.

# Article オブジェクトから、リレーションを張っている Reporter にアクセ
# スできる API を使えるようになります。
>>> r = a.reporter
>>> r.full_name
'John Smith'

.. # And vice versa: Reporter objects get API access to Article objects.

# 逆も可能です: Reporter オブジェクトから Article オブジェクトにアクセスできます。
>>> r.article_set.all()
[<Article: Django is cool>]

.. # The API follows relationships as far as you need, performing efficient
   # JOINs for you behind the scenes.
   # This finds all articles by a reporter whose name starts with "John".

# API は必要に応じてリレーションを辿ります。背後では効率的に JOIN を
# 行います。
# "John" ではじまる Reporter の全ての Article を検索してみましょう。
>>> Article.objects.filter(reporter__full_name__startswith="John")
[<Article: Django is cool>]

.. # Change an object by altering its attributes and calling save().

# 属性値を変更して save() すればオブジェクトを変更できます。
>>> r.full_name = 'Billy Goat'
>>> r.save()

.. # Delete an object with delete().

# delete() でオブジェクトを削除できます。
>>> r.delete()

作業場 (scaffold) ではなく完成品 (whole house) の、動的な管理インタフェース

モデルを定義したら、 Django は玄人向きの実運用に耐える 管理インタフェー ス (admin interface) を自動的に生成します。 管理インタ フェースとは、認証をパスしたユーザがオブジェクトを追加、変更、削除できる Web サイトです。管理インタフェースの作成は簡単で、モデルクラスを admin に追 加するだけです:

.. # In models.py...

# models.py には以下のように書きます

from django.db import models

class Article(models.Model):
    pub_date = models.DateTimeField()
    headline = models.CharField(max_length=200)
    content = models.TextField()
    reporter = models.ForeignKey(Reporter)

.. # In admin.py in the same directory...

# 同じディレクトリの admin.py には以下のように書きます

import models
from django.contrib import admin

admin.site.register(models.Article)

サイトの編集はスタッフ、顧客、もしくはあなた自身の手で行われるものであり、 コンテンツの管理だけのためにバックエンドインタフェースを作りたくはない、 という思想がここにはあります。

作者たちが Django アプリケーションを作成するときの典型的なワークフローは、 モデルを作成し、 admin サイトを組み上げてできるだけ早期に立ち上げ、スタッフ (や顧客) がデータを投入できるようにしておいてから、データを公開するための方 法を開発してゆくというものです。

自由な URL 設計

すっきりとして洗練された URL スキームは、高品質な Web アプリケーションを実 現する上で重要な要素です。 Django は美しい URL の設計を助け、 .php.asp のようなお粗末なゴミを URL に入れさせません。

特定のアプリケーション用の URL を設計するには、 URLconf と呼ばれる Python モジュールを一つ作成します。 URLconf はいわばアプリケーションの目次にあたり、 URL のパターンを Python のコールバッ ク関数に対応づけています。 URLconf はまた、 URL を Python コードと脱カップ リングする働きを持っています。

Reporter/Article の例では、 URLconf は以下のようになります:

from django.conf.urls import patterns, url, include

urlpatterns = patterns('',
    (r'^articles/(\d{4})/$', 'news.views.year_archive'),
    (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
    (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)

上のコードは簡単な正規表現で書かれた URL を Python コールバック関数 (ビュー: view) に対応づけています。正規表現の中で丸括弧を使い、 URL から値を「取り込 み」ます。ユーザがあるページをリクエストすると、 Django は全ての正規表現に わたって順に調べてゆき、最初に URL にマッチするパターンで止まります。 (マッ チする正規表現がなければ Django は独自の 404 ビューを呼び出しします)。 正規 表現をロード時にコンパイルしておくので、この処理は極めて高速です。

正規表現が URL にマッチすると、 Django は指定されているビューを import して 呼び出します。ビューは単純な Python の関数です。各ビューにはリクエストオブ ジェクトが渡されます。リクエストオブジェクトにはリクエストのメタデータと、 正規表現で取り込んだ値が渡されます。

例えば、ユーザが “/articles/2005/05/39323/” という URL をリクエストすると、 Django は news.views.article_detail(request, '2005', '05', '39323') のような関数呼び出しを行います。

ビューの自作

各ビュー (view) には二つの役割があります: 一つはリクエストされたページのコ ンテンツを含む HttpResponse オブジェクトを返すこと、 もう一つは Http404 のような例外の送出です。それ以外の 処理はユーザ次第です。

一般的に、ビューはパラメタに従ってデータベースからデータを取り出し、テンプ レートをロードして、取り出したデータでテンプレートをレンダリングします。 上の year_archive のビューを例に示しましょう:

def year_archive(request, year):
    a_list = Article.objects.filter(pub_date__year=year)
    return render_to_response('news/year_archive.html',
                              {'year': year, 'article_list': a_list})

この例では Django の テンプレートシステム を使っ ています。テンプレートシステムは、強力な機能をいくつも備えながらも、非プロ グラマが使いこなせる程度に簡単な仕組みです。

テンプレートの設計

上のコードでは news/article_detail.html という名前のテンプレートをロー ドしています。

Django にはテンプレート検索パスという概念があり、テンプレートが冗長になるの を防いでいます。 Django の設定で、テンプレートを探すディレクトリのリストを 設定しておきます。あるディレクトリにテンプレートが見つからなければ、Django は次のディレクトリ、また次のディレクトリとテンプレートを探します。

さて、 news/year_archive.html が見つかったとしましょう。テンプレート は以下のように書かれています:

{% extends "base.html" %}

{% block title %}{{ year }}年の記事{% endblock %}

{% block content %}
<h1>{{ year }}年の記事</h1>

{% for article in article_list %}
<p>{{ article.headline }}</p>
<p>By {{ article.reporter.full_name }}</p>
<p>作成日: {{ article.pub_date|date:"F j, Y" }}</p>
{{ article.article }}
{% endfor %}
{% endblock %}

変数は二重の波括弧で囲まれています。 {{ article.headline }} は、 「article の headline という属性の出力」を表しています。とはいえ、ドット表 記は属性の検索に使われるだけではありません。辞書の検索や、インデクス指定、 関数呼び出しも行えます。

{{ article.pub_date|date:"F j, Y" }} で、 Unix スタイルの「パイプ」 (文字 “|”) を使っていることに注意して下さい. これはテンプレートフィルタ と呼ばれ、変数の値にフィルタをかけるためのものです。この例では、フィルタに よって Python の datetime オブジェクトを指定の形式にフォーマットしています (PHP の date 関数に似ていますね。そう、これは PHP の便利なところです)。

フィルタは好きなだけ連鎖させられます。カスタムのフィルタも実装できます。 カスタムのテンプレートタグを設計でき、背後で自作の Python コードを実行でき ます。

最後に、Django にはテンプレートの継承という概念があります: 継承を宣言してい るのは {% extends "base.html" %} の部分です。このタグは「まず ‘base.html’ というテンプレートをロードせよ。このテンプレートにはいくつかの ブロックが定義されているが、それらのブロックの中身を以下のブロック定義で埋 めよ」という命令です。要するに、テンプレートを継承すると、各テンプレートご とに固有の定義だけを記述すればよくなり、テンプレート間の冗長性が劇的に減る のです。

“base.html” テンプレートは以下のように書けます:

<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <img src="sitelogo.gif" alt="Logo" />
    {% block content %}{% endblock %}
</body>
</html>

このテンプレートはサイトのルック & フィール (とサイトのロゴ) を定義するだけ にまで、極度に単純化されています。また、子テンプレートで埋めるための「穴」 を提供しています。これによって、ベーステンプレート一つを変更するだけでサイ ト全体のデザインを簡単に変更できます。

また、子テンプレートを変えずにベーステンプレートだけを変えた複数バージョン のサイトも作成できます。 Django の作者たちはこのテクニックを使い、新しい テンプレートを作成するだけで携帯電話向けのまったく見栄えの違うサイトを 作成してきました。

他のシステムを使いたければ、必ずしも Django のテンプレートシステムを使う必 要はないということに注意してください。 Django のテンプレートシステムは Django のモデルレイヤと部分的にしっかり組み合わさっていますが、絶対に使わね ばならないということではありません。さらに言えば、 Django のデータベース API を使う必然性もありません。別のデータベース抽象化レイヤを使っても構いま せんし、 XML ファイルやディスク上のファイルを読み込んでも構いません。何でも やりたいことをできるのです。Django の構成要素 – モデル、ビュー、テンプレー ト – は、互いに切り離して利用できるのです。

これらはほんの一部にすぎません

以上、 Django の機能についてざっと紹介してきました。 Django は他にもまだま だ便利な機能を備えています:

  • 自動生成される admin のセクシーな機能の数々。ここで紹介したのはほんの 表層の一部でしかありません。

次は、あなたが Django をダウンロード して、 チュートリアル を読み、 コミュニティ に参加す る番です。ご精読ありがとうございました!

インストールガイド

revision-up-to:17812 (1.4)

Django を動かすには、まずインストールせねばなりません。ここでは、 Django を 学んでみるにあたって必要な、簡単で最小限のインストール方法を示します。 色々なインストール方法をカバーしている 詳細なインストールガイド も用意してあります。

Python のインストール

Django は Python の Web フレームワークなので Python が必要です。 Django は バージョン 2.5 から 2.7 までの Python で動作します (Python 3.0 には以前のバー ジョンと互換性のない変更が加わっているので、 Django は現在のところ Python 3.0 で動作しません。サポートされている Python のバージョンと、 3.0 への移行 に関する情報は FAQ を参照してください)。 Python 2.5 から は、 SQLite という軽量なデータベースが付属していて、すぐにデータベースの セットアップをすることなく開発できます。

http://www.python.org から Python を取ってきましょう。 Linux や Mac OSX を 動かしているのなら、おそらくインストール済みのはずです。

Jython で Django を動かす

Jython (Java プラットフォームで動く Python 実装) を使っているなら、他 にもいくつかステップを踏む必要があります。詳しくは Jython 上で Django を動かす を参照してください。

インストールされている Python のバージョンを調べるには、シェル上で python と入力します。出力は以下のようになるでしょう:

Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

データベースをセットアップする

バージョン 2.5 以降の Python をインストールしていれば、この節は読み飛ばして もかまいません。

バージョン 2.5 よりも前の Python を使っているか、 PostgreSQL や MySQL, Oracle といった、SQLite 以外の「大掛かりな」データベースエンジンを扱いたけ れば、 データベースのインストールに関する情報 を参照してください。

旧バージョンの Django の除去

以前のバージョンからアップグレードする形で Django をインストールする場合、 新しいバージョンをインストールする前に、まず旧バージョンをアンインス トールしておく 必要があります。

Django のインストール

3 通りの簡単な方法で、Django をインストールできます:

常に使用している Django のバージョンと一致するドキュメントを 参照するようにしてください。

上に挙げた選択肢のうち、最初の二つを選んだ場合は、ドキュメントを読む際 に「 開発版で登場した機能です 」とマークされた部分に気をつけてくだ さい。この文は、開発版の Django でのみ利用可能な機能をマークしています。 そうした機能は、公式リリースでは動かないでしょう。

確認

Django が Python から使用できるか確認するために、シェル上で python と 入力します。 Python プロンプトの中で、 Django を import してみます:

>>> import django
>>> print django.get_version()
1.4

以上です!

これだけです。さあ、 チュートリアルをやってみましょう.

はじめての Django アプリ作成、その 1

revision-up-to:17812 (1.4)

さあ、例を交えながら学んでゆきましょう。

このチュートリアルでは、簡単な投票 (poll) アプリケーションの作成に取り組ん でもらいます。

Poll アプリケーションは、

  • ユーザが投票したり結果を表示したりできる公開用サイト
  • 投票項目の追加、変更、削除を行うための管理 (admin) サイト

の二つの部分に分かれています。

Django は既にインストール済み として、説明を進めます。 Django がインストールされているかどうかは、Python 対話シェルを起動して import django を実行してみればわかります。エラーなく import できるなら、 Django はインストールされています。

困ったときは:

このチュートリアルを進めてゆく上で困ったことがあったら、 django-usersirc.freenode.net#djangoチャネル で誰か助けてくれそ うな人と話してみてください。

プロジェクトの作成

初めて Django を使うのなら、最初のセットアップを行う必要があります。通常は、 Django の プロジェクト (project) を構成するコードを自動生成 します。プロジェクトとは、データベースの設定や Django 固有のオプション、ア プリケーション固有の設定などといった、個々の Django インスタンスの設定をあ つめたものです。

コマンドラインから、コードを置きたい場所に cd して、以下のコマンドを 実行してください。

django-admin.py startproject mysite

現在のディレクトリに mysite ディレクトリが作成されます。

ディストリビューションパッケージでスクリプトの名前が違うかも

もし apt-get や yum のような Linux ディストリビューションのパッケージ マネージャを使って Django をインストールした場合、 django-admin.pydjango-admin に名前が変更されている場合があります。その場合は、 これ以降のドキュメント内で出てくるそれぞれのコマンドから .py を削除 して操作を続けてください。

Max OS X でのパーミッションに関するエラー

Mac OS X を使っている場合、 django-admin.py startproject を実行しよ うとすると、 “permission denied” というメッセージが出ることがあります。 OS X のような Unix ベースのシステムでは、ファイルをプログラムとして実行 したい場合に、ファイルに「プログラムとして実行可能」というマークをつけて おく必要があるためです。ファイルに実行可能マークをつけるには、 Terminal.app を起動して、 django-admin.py を収 めているディレクトリに ( cd コマンドで) 移動して、 sudo chmod +x django-admin.py を実行してください。

Note

プロジェクトの名前を付けるとき、組み込みの Python モジュールや Django のコンポーネントの名前を使わないようにしてください。とりわけ、 django (Django 自体と名前が衝突します) や test (組み込みの Python パッケージ名と名前が衝突します) を使わないようにしましょう。

python setup.py ユーティリティで Django をインストールしたのなら、 django-admin.py はシステムパスのどこかにあるはず です。パス上になければ、 site-packages/django/bin にあります。 site-packages は Python インストールディレクトリの中にあります。パス上 のどこか、例えば /usr/local/bindjango-admin.py へのシンボリックリンクを張って おきましょう。

コードはどこに置くの?

PHP の経験があるなら、これまでは Web サーバのドキュメントルート下 (/var/www といった場所) にコードを配置してきたことでしょう。 Django ではそうする必要はありません。むしろ Python コードをドキュメントルート 下に置くのは賢明ではありません。コードをドキュメントルート下に置くと、 誰かがコードを Web を介して読めるようになってしまうからです。これは安全 上よろしくありません。

コードはドキュメントルートの 、例えば /home/mycode の ような場所に置きましょう。

startproject が何を作成したかをみてみましょう:

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

自分のレイアウトと違う場合

デフォルトのプロジェクトのレイアウトが最近変わりました。もし、フラットな レイアウトの場合 (内側の mysite/ ディレクトリがない場合) は、この チュートリアルのバージョンとは違う Django のバージョンを使用していること でしょう。古いチュートリアルを参照するか、新しいバージョンの Django を 入手してください。

ファイルはそれぞれ以下のような役割を持っています:

  • 外側の mysite/ ディレクトリは、このプロジェクトのただの入れ物です。 名前は Django に関係しませんので、好きな名前に変更できます。
  • manage.py: Django プロジェクトに対する様々な操作を行うための コマンドラインユーティリティです。詳しくは django-admin.py と manage.py を参照してください。
  • 内側の mysite/ ディレクトリは、このプロジェクトの本当の Python パッケージです。この名前が Python パッケージの名前であり、 import の際に 使用する名前です (例えば import mysite.settings) 。
  • mysite/__init__.py: このディレクトリが Python パッケージであることを Python に知らせるための空のファイルです。(Python の初心者は、 Python の公式 ドキュメントの パッケージの詳しい説明 を読んで下さい。)
  • mysite/settings.py: Django プロジェクトの設定ファイルです。 設定の仕組みは Django の設定 を参照してください。
  • :file:mysite/urls.py: Django プロジェクトの URL 宣言、いうなれば Django サイトにおける「目次」に相当します。詳しくは URL ディスパッチャ を参照 してください。
  • mysite/wsgi.py: WSGI互換のある Web サーバでプロジェクトを動かすための エントリーポイントです。詳しくは WSGI 環境にデプロイする方法 を参照 してください。
開発用サーバ

プロジェクトがうまく動作するか確かめましょう。外側の mysite ディレク トリに移り、 python manage.py runserver を実行してください。以下のような メッセージが表示されるはずです:

Validating models...
0 errors found.

Django version 1.4, using settings 'mysite.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

これで、 Django 開発サーバを起動しました。 Django 開発サーバは Python だけ で書かれた軽量な Web サーバです。このサーバは、開発を迅速に行い、運用に適し た状態になるまで Apache のような運用サーバの設定をいじらなくても良いように するためのものです。

ここでちょっと注意しておきましょう。このサーバは開発中の利用だけを考えて作 られているため、絶対に運用環境では使わないようにしてください (筆者たちの専 門は Web フレームワークであって、 Web サーバではありません)。

さあ、これでサーバが起動したので、ブラウザで http://127.0.0.1:8000/ にアク セスしてみてください。 “Welcome to Django” と表示された、明るいパステル調の ライトブルーのページが出るはずです。やったね!

ポート番号の変更

デフォルトでは、 runserver コマンドを実行すると、開発用サー バはポート番号 8000 で起動します。サーバのポート番号を変更したければ、 コマンドライン引数で指定します。例えばポート番号を 8080 にしたければ以 下のようにしてください:

python manage.py runserver 8080

サーバの IP を指定するときには、ポート番号も一緒に指定します。従って、 全ての IP からのリクエストを受け付ける (サーバを他のコンピュータから可 視にする) には、以下のようにします:

python manage.py runserver 0.0.0.0:8000

開発サーバの詳細な説明は runserver のリファレンスを参照して ください。

Database の設定

それでは、 mysite/settings.py を編集しましょう。 mysite/settings.py は Django の設定を表現する通常の Python モジュール です。 DATABASES 'default' の中の以下のキーを書き換えて、お使 いのデータベースへの接続パラメタに合わせましょう:

  • ENGINE'django.db.backends.postgresql_psycopg2', 'django.db.backends.mysql', 'django.db.backends.sqlite3' または 'django.db.backends.oracle' のいずれかです。 他にも いくつか あります。

  • NAME` – データベースの名前です。 SQLite を使っている場合には データベースファイルのフルパス (絶対パス) にします。 指定したパスのファイルが存在しなければ、 Django は最初にデータベースの同期 を実行したときにファイルを生成します (後で解説します)。

    パスを指定するときには、 Windows 環境でも必ずスラッシュ (/) を区切り文字 に使ってください (例: C:/homes/user/mysite/sqlite3.db)

  • USER – データベースのユーザ名です (SQLite では使いません)。

  • PASSWORD – データベースのパスワードです。 (SQLite では使いません)。

  • HOST – データベースのあるホストです。データベースサーバが物理的に 同じマシン上にあるのなら空文字列にしておきます。(SQLite では使いません)。

データベースをあまり扱ったことがないのなら、 ENGINE'django.db.backends.sqlite3' を指定して SQLite を使用することをお勧めしま す。 SQLite はバージョン 2.5 以降の Python に組み込まれているので、特にインス トールする必要がありません。

Note

PostgreSQL や MySQL を使っている場合、この時点でデータベースを作成して おいてください。データベースを作成するには、データベースの対話プロンプ トで “CREATE DATABASE database_name;” を実行します。

SQLite を使う場合には、予め何か作成しておく必要はありません。データベー スファイルは、必要に応じて自動的に生成されます。

settings.py を編集する際、 TIME_ZONE にタイムゾーンをセット してください。デフォルト値はアメリカのセントラルタイムゾーン (シカゴ) になり ます。また、ファイルの末尾近くにある INSTALLED_APPS 設定に注意して ください。この変数には、現在の Django インスタンスで有効な全ての Django アプリ ケーションの名前が入ります。アプリケーションは複数のプロジェクトで利用でき、 配布もできます。

デフォルトでは INSTALLED_APPS には以下のアプリケーションが入って います。これらのアプリケーションはいずれも Django に付属のものです:

これらの機能はよく使われるのでデフォルトで付属しています。

上に挙げたアプリケーションは、必ず少なくとも一つのデータベーステーブルを使 います。そこで、アプリケーションを使う前にテーブルを作成しておく必要があり ます。テーブルを作成するには以下のコマンドを使います:

python manage.py syncdb

syncdb コマンドは INSTALLED_APPS 設定を探し、 settings.py のデータベース設定に従ってデータベース上に必要なテーブ ルを作成します。コマンドが生成したデータベースを示すメッセージが表示され、 認証システムで使うスーパユーザアカウントを作成したいかどうか尋ねるプロンプ トが出ます。アカウントを作成しておきましょう。

Django がどんなテーブルを作成したか興味があるなら、データベースのコマンドラ インクライアントを使って、 \dt (PostgreSQL), SHOW TABLES; (MySQL), あるいは .schema (SQLite) と入力してみましょう。

ミニマリストのために

上で述べたように、デフォルトのアプリケーションはよくあるケースに対応す るために入っているにすぎず、誰もが必要としているわけではありません。デ フォルトアプリケーションの一部なり全部なりが必要なければ、 syncdb を実行する前に該当する行をコメントアウトするか削除し てかまいません。 syncdb コマンドは INSTALLED_APPS にあるアプリケーションのテーブルを生成しているにすぎません。

モデルの作成

さあ、これで自分用の環境、すなわちプロジェクトが立ち上がり、作業にとりかか る準備ができました。

Django で書いたアプリケーションは Python パッケージからなり、 ある規約に従っ て Python パス のどこかに置かねばなりません。Django にはアプリケーション の基本的なディレクトリ構造を作成するためのユーティリティがついてくるので、 ディレクトリの作成は気にせずコードの記述に集中できます。

プロジェクトとアプリケーション

プロジェクトとアプリケーションの違いとは何でしょうか?アプリケーション とは、実際に何らかの処理を行う Web アプリケーションを指します。例えばブ ログシステムや公開レコードのデータベース、単純な投票アプリといった具合 です。プロジェクトとは、あるウェブサイト向けに設定とアプリケーションを 集めたものです。一つのプロジェクトには複数のアプリケーションを入れられ ます。また、一つのアプリケーションは複数のプロジェクトで使えます。

このチュートリアルでは、簡単のため、投票アプリケーションを mysite ディレクトリの中に作ります。その結果、アプリケーションはプロジェクトとカッ プリングします。すなわち、 poll アプリケーション内の Python コードは mysite.polls のように参照されることになります。チュートリアルの後半では、 アプリケーションを配布用に脱カップリングする方法について議論する予定です。 アプリケーションは、 Python パス のどこにでも置くことができます。この チュートリアルでは、投票アプリケーションを manage.py ファイルのすぐ 隣に作り、 mysite のサブモジュールというより、自身のトップレベルのモジュ ールとして import できるようにします。

アプリケーションを作成するには、 manage.py と同じディレクトリに入っ て、以下のようなコマンド:

python manage.py startapp polls

を入力します。このコマンドは polls というディレクトリを作成し、その 中に以下のようにファイルを配置します:

polls/
    __init__.py
    models.py
    tests.py
    views.py

このディレクトリ構造こそが、 poll アプリケーションの全体像です。

Django でデータベース Web アプリケーションを書くための最初のステップは、モ デルの定義です。本質的には、データベースのレイアウトと、追加のメタデータの 定義です。

設計哲学

モデルは、手持ちのデータに対する唯一 (single) の決定的な (definitive) ソースです。モデルには自分が格納したいデータにとって必要不可欠なフィー ルドと、そのデータの挙動を収めます。 Django は DRY 則 に従っ ています。Django のモデルの目的は、ただ一つの場所でデータモデルを定義し、 そこから自動的にデータを取り出すことにあります。

これから開発する簡単な poll アプリケーションでは、投票項目 (poll) と選択肢 (choice) の二つのモデルを作成します。 poll には質問事項 (question) と公開日 (publication date) の情報があります。 choice には選択肢のテキストと投票数 (vote) という二つのフィールドがあります。各 choice は一つの poll に関連づけ られています。

Django では、こうした概念を簡単な Python クラスで表現できます。 polls/models.py ファイルを以下のように編集してください:

from django.db import models

class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(max_length=200)
    votes = models.IntegerField()

コードは単純明解ですね。各モデルは一つのクラスで表現され、いずれも django.db.models.Model のサブクラスです。各モデルには複数のクラス 変数があり、個々のクラス変数はモデルのデータベースフィールドを表現していま す。

各フィールドは Field クラスのインスタンスとして 表現されています。例えば、 CharField は 文字のフィールドで、 DateTimeField は日時フィー ルドです。こうしたクラスは、各フィールドにどのようなデータ型を記憶させるか を Django に教えます。

models.*Field` インスタンスの名前 (questionpub_date) はフィールドの名前で、計算機にとって扱いやすい名前を付けます。この名前は Python コードの中で使いますし、データベースではカラム名に使います。

django.db.models.Field の第一固定引数には、オプションとして人間可 読なフィールド名も指定できます。このフィールド名は Django の二つの内省 (introspection) 機能で使う他、ドキュメントとしての役割も果たします。人間可 読なフィールド名を指定しない場合、 Django は機械可読な名前を使います。上の 例では、 Poll.pub_date にだけ人間可読なフィールド名を指定しました。モデ ルの他のフィールドでは、フィールドの機械可読な名前は人間可読な名前としても 十分なので定義していません。

Field クラスの中には必須の引数を持つものがありま す。例えば CharField には max_length を指定する必要があります。この引 数はデータベーススキーマで使われる他、後で述べるバリデーションでも使われま す。

最後に、 ForeignKey を使ってリレーションが定義さ れていることに注意して下さい。このリレーションは、各 Choice が一つの Poll に関連づけられていることを Django に教えます。 Django は多対一、多対多、一 対一といった、広く使われているリレーション全てをサポートしています。

モデルを有効にする

前述のようなほんのわずかなコードをモデルに書くだけで、 Django はたくさんの 情報を手にします。このコードを使って、 Django は:

  • アプリケーションのデータベーススキーマを作成 (CREATE TABLE 文を実 行) できます。
  • Poll や Choice オブジェクトに Python からアクセスするためのデータベー ス API を作成できます。

ただし、その前に polls アプリケーションをインストールしたことをプロジェ クトに教えてやる必要があります。

設計哲学

Django アプリケーションは「プラガブル (pluggable)」です。アプリケーショ ンは特定の Django インストールに結び付いていないので、アプリケーション を複数のプロジェクトで使ったり、単体で配布したりできます。

再度 settings.py ファイルを編集して、 INSTALLED_APPS 設 定を変更し、 'polls' を入れます。以下のようになるはずです:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    # 'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'polls',
)

これで Django は polls アプリケーションが入っていることを知りました。 もう一つコマンドを実行してみましょう:

python manage.py sql polls

以下のような (polls アプリケーション用の CRATE TABLE SQL 文) が表示されるは ずです:

BEGIN;
CREATE TABLE "polls_poll" (
    "id" serial NOT NULL PRIMARY KEY,
    "question" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id") DEFERRABLE INITIALLY DEFERRED,
    "choice" varchar(200) NOT NULL,
    "votes" integer NOT NULL
);
COMMIT;

以下の点に注意してください:

  • 実際に出力される SQL 文は、使っているデータベースによって変わります。
  • テーブル名はアプリケーションの名前 (polls) とモデルの小文字表記 (poll および choice) を使って自動的に生成されます (この挙動は オーバライドできます。)
  • 主キー (primary key, ID) は自動的に生成されます (この挙動もオーバライ ド可能です)
  • 便宜上、 Django は外部キーのフィールド名に "_id" を追加します。も ちろんこの挙動もオーバライド可能です。
  • 外部キーのリレーションは REFERENCES 文で明示的に作成されます。
  • SQL 文は使っているデータベースに応じて細かく調整されます。従って、 auto_increment (MySQL)、 serial (PostgreSQL)、 integer primary key (SQLite) といったデータベース固有のフィールド タイプは自動的に指定されます。クオートの仕方、すなわち一重と二重のど ちらの引用符を使うか、といったことも自動で調整します。このチュートリ アルの作者は PostgreSQL を使っており、例題での出力は PostgreSQL の文 法に準じています。
  • sql コマンドを実行しても、実際にデータベースで SQL を実行 するわけではありません。 sql コマンドは、ユーザが Django の挙動を 知りたいと考えたときのため、単に SQL 文をスクリーンに表示しているだけ です。必要なら、この SQL 文をコピーしてデータベースクライアントのプロ ンプトにペーストできますが、後ですぐ述べるように、 Django では SQL を データベースに commit させる簡単な方法を提供しています。

興味があるなら、以下のコマンドも実行してみてください:

  • python manage.py validate – モデルの構成にエ ラーがないか調べます。
  • python manage.py sqlcustom polls – 各アプリケー ション向けに定義しておいた、カスタマイズ (テーブル形式の変更や制約) 用の SQL 文を出力します。
  • python manage.py sqlclear polls – アプリケーショ ン用のテーブルのうち、データベース上に存在するものについて必要に応じ て DROP TABLE 文を出力します。
  • python manage.py sqlindexes polls – アプリケー ション用の CREATE INDEX 文を出力します。
  • python manage.py sqlall polls – ‘sql’, ‘sqlcustom’, ‘sqlindexes’ コマンドを合わせたものです。

これらのコマンドの出力を見れば、水面下で実際に行われていることを理解する助 けになるでしょう。

syncdb を再度実行して、モデルテーブルをデータベース上に作成しま しょう:

python manage.py syncdb

syncdb コマンドは INSTALLED_APPS に登録されているアプ リケーションのうち、データベース上にまだ存在しないものに対して sqlall で生成した SQL を生成します。これによって、最後に syncdb を実行した時以後に新たにプロジェクトに追加されたアプリケー ションのテーブルと初期データ、インデクスを生成します。 syncdb はその都度存在しないテーブルだけを生成するので、繰り返し実行してもかまいま せん。

manage.py ユーティリティでできることについては django-admin.py のドキュメント を読んで下さい。

API で遊んでみる

さて、Python 対話シェルを起動して、 Django が提供する API で遊んでみましょ う。 Python シェルを起動するには、以下のコマンドを実行します:

python manage.py shell

単に “python” を実行しないのは、 Django に settings.py ファイルへの import パスを与える DJANGO_SETTINGS_MODULE の環境変数を manage.py で設定しているからです。

manage.py を使わずに済ませる方法

manage.py を使いたくなくても、問題はありません。環境変数 DJANGO_SETTINGS_MODULEmysite.settings に設定して、 manage.py と同じディレクトリで python を実行してください (または import mysite が通るように、ディレクトリが Python のパス上 にあるようにしてください) 。

詳しくは django-admin.py のドキュメント を参 照してください。

シェルに入ったら、 データベース API の世界を探検 してみましょう:

>>> from polls.models import Poll, Choice # モデルクラスを import します。

# まだ Poll は一つもできていません。
>>> Poll.objects.all()
[]

# 新たな Poll を作成しましょう。
# デフォルト設定ファイルでタイムゾーンへのサポートが使用可能になって
# いるので、 Django は pub_date に対して tzinfo を伴った datetime を
# 期待します。 datetime.datetime.now() の代わりに timezone.now() を使用
# してください。
>>> from django.utils import timezone
>>> p = Poll(question="What's new?", pub_date=timezone.now())

# 出来たオブジェクトをデータベースに保存します。 save() は明示的に呼ば
# ねばなりません。
>>> p.save()

# これでオブジェクトに ID が割り当てられました。お使いのデータベースに
# よっては、この値は "1" ではなく "1L" のときもあります。心配することは
# ありません。単にデータベースバックエンドが Python 長整数型で値を返す
# ようになっているだけのことです。
>>> p.id
1

# データベースの各カラムに Python の属性としてアクセスします。
>>> p.question
"What's new?"
>>> p.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# 属性を変更して save() を呼び出すとカラムの値を変更します。
>>> p.question = "What's up?"
>>> p.save()

# objects.all() はデータベース上の全ての Poll を返します。
>>> Poll.objects.all()
[<Poll: Poll object>]

おっと、ちょっと待って下さい。 <Poll: Poll object> なんて全然親切な表現 ではありませんね。そこで (polls/models.py ファイルに定義されている) polls 関係のモデルを少し修正して、 PollChoice__unicode__() メソッドを追加しましょう:

class Poll(models.Model):
    # ...
    def __unicode__(self):
        return self.question

class Choice(models.Model):
    # ...
    def __unicode__(self):
        return self.choice

__unicode__() をモデルに追加しておく重要性は、 対話プロンプトで扱うときに精神的によいだけでなく、Django が自動生成する管理 インタフェースのいたるところでオブジェクトの表現 (representation) が使われ ているという点にもあります。

なぜ __str__() ではなく __unicode__() を使うの?

Python に詳しければ、普段は __str__() で はなく __unicode__() を実装していることで しょう。 __unicode__() を使うのは、Django のモデルがデフォルトで Unicode を扱うからです。 Django では、データベー ス上に保存された文字列の情報は、取り出すときに全て Unicode 型に変換され ます。

Django のモデルは、デフォルトで __str__() メソッドを実装していて、中で __unicode__() を呼び出して、得た結果を UTF-8 のバイト文字列に変換しています。従って、 unicode(p) は Unicode 文字列を返し、 str(p) は UTF-8 でエンコードされた通常の文字 列を返します。この仕様がよくわからなければ、とにかく __unicode__() をモデルに追加するのだと覚 えておいてください。なにはともあれ、それでうまく動作します。

__unicode__() は通常の Python メソッドという ことに注意してください。デモ用にカスタムのメソッドを追加してみましょう:

import datetime
from django.utils import timezone
# ...
class Poll(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

import datetimefrom django.utils import timezone で Python の 標準モジュール datetime と Django のタイムゾーン関連ユーティリティの django.utils.timezone を参照していることに注意してください。もし Python でタイムゾーンを取り扱うことに不慣れな場合は、 タイムゾーン で勉強できます。

python manage.py shell を実行して、Python 対話シェルに戻りましょう:

>>> from polls.models import Poll, Choice

# __unicode__() がきちんと働いていることを確認します。
>>> Poll.objects.all()
[<Poll: What's up?>]

# Django は様々なデータベース照合 API を提供しています。 API はキーワー
# ド引数で隅々まで操作できます。
>>> Poll.objects.filter(id=1)
[<Poll: What's up?>]
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]

# 2012 年の Poll を取り出しましょう。
>>> Poll.objects.get(pub_date__year=2012)
<Poll: What's up?>

>>> Poll.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Poll matching query does not exist.
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]

# 主キーの照合はよくあることなので、 Django は主キーの厳密一致を照合
# するショートカットを提供しています。
# 以下の実行文は Poll.objects.get(id=1) と同じです。
>>> Poll.objects.get(pk=1)
<Poll: What's up?>

# カスタムメソッドが動作するか確かめてみましょう。
>>> p = Poll.objects.get(pk=1)
>>> p.was_published_recently()
True

# Poll に二つの Choice を指定しましょう。 create を呼び出すと、新たな
# Choice オブジェクトを生成し、 INSERT 文を実行し、 Poll からアクセス可
# 能な Choice オブジェクトの集合に追加して、新たに作成された Choice オ
# ブジェクトを返します。 Django は API を通してアクセス出来る "あちら側"
# の外部キー (例えば poll の choice) を保持する set を作ります。
>>> p = Poll.objects.get(pk=1)

# 関連するオブジェクトの set から choice を表示します。現在は空です。
>>>> p.choice_set.all()
[]

# 3つの choice を作ります。
>>> p.choice_set.create(choice='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice='Just hacking again', votes=0)

# Choice オブジェクトは自分に関連づけされた Poll オブジェクトに
# アクセスするための API を備えています。
>>> c.poll
<Poll: What's up?>

# 逆も行えます: Poll オブジェクトから Choice オブジェクトにアクセスでき
# ます。
>>> p.choice_set.all()
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
>>> p.choice_set.count()
3

# API は必要に応じて自動的にリレーションを追跡します。リレーションを辿
# るには二重アンダースコアを使います。この表記法には制限がなく、何段階
# でも連鎖できます。以下の例では、 pub_date が 2012 の全ての Poll に関
# 連づけられている Choice を返します。
>>> Choice.objects.filter(poll__pub_date__year=2012)
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]

# choice を一つ削除しましょう。 delete() を使います。
>>> c = p.choice_set.filter(choice__startswith='Just hacking')
>>> c.delete()

リレーションモデルの詳細は、 リレーションオブジェクトリファレンス を 参照してください。 API を通じたフィールドの照合のためのダブルアンダースコア の使い方の詳細は、 フィールドの照合 を参照してください。 データベース API の詳細は、 データベース API リファレンス を参照してください。

API を使いこなせるようになったら、 チュートリアルその 2 に進んで、Django が自動生成 する管理インタフェースを動かしてみましょう。

はじめての Django アプリ作成、その 2

revision-up-to:17812 (1.4)

このチュートリアルは チュートリアルその 1 の続き です。ここでは、引続き Web 投票アプリケーションの開発を例にして、Django が 自動生成する管理サイト (admin) を中心に解説します。

設計哲学

コンテンツの追加や変更、削除を行うためのスタッフや顧客向けの管理サイト 構築は、創造性の欠けた退屈なものです。そこで、 Django ではモデルを管 理するためのインタフェース生成を完全に自動化しています。

Django はニュースルーム環境で開発されました。ニュースルーム環境では、 「コンテンツの作成者 (content publisher) 用」と「公開用 (public) 」サイ トをきわめて明確に区別しています。サイト管理者は新たな話題やイベント、 スポーツのスコアなどの入力に使い、コンテンツは公開用サイト上で表示され ます。 Django は、サイト管理者向けの一元化されたコンテンツ編集インタフェー スの提供という問題を解決しているのです。

admin は一般のサイト訪問者に使ってもらうためのものではなく、サイト管理 者のためのものです。

管理サイトの有効化

デフォルトでは、 Django の管理サイトは無効化されていて、自分で選択して有効 にせねばなりません。 admin を有効にするには、以下の 3 つの作業が必要です:

  • INSTALLED_APPS 設定の "django.contrib.admin" をコメント アウトを解除します。

  • python manage.py syncdb を実行します。新たなアプリケーションを INSTALLED_APPS に追加したので、データベースを更新せねばな りません。

  • mysite/urls.py ファイルを編集し、 admin に関連する3行のコメントアウト を解除します。このファイルは URLconf といいます。 URLconf についてはチュー トリアルの次の部で解説します。今はただ、この設定が URL をアプリケーション に対応づけていることだけを覚えておきましょう。最終的に、 urls.py は 以下のようになるはずです:

    from django.conf.urls import patterns, include, url
    
    # Uncomment the next two lines to enable the admin:
    from django.contrib import admin
    admin.autodiscover()
    
    urlpatterns = patterns('',
        # Examples:
        # url(r'^$', '{{ project_name }}.views.home', name='home'),
        # url(r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),
    
        # Uncomment the admin/doc line below to enable admin documentation:
        # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
    
        # Uncomment the next line to enable the admin:
        url(r'^admin/', include(admin.site.urls)),
    )
    

    (太字の行はコメントを外した部分です)

開発サーバの起動

開発用サーバを起動して、管理サイトを探検してみましょう。

チュートリアルその 1 で、開発サーバを以下のように起動したのを思い出してくだ さい:

python manage.py runserver

次はブラウザを起動して、ローカルドメインの “/admin/” 、つまり http://127.0.0.1:8000/admin/ にアクセスします。以下のような admin のログイ ン画面が表示されるはずです:

Django 管理サイトのログイン画面

自分の画面と違う場合

もしこの時点で、上のログイン画面の代わりに以下のようなエラーの画面が 表示された場合は:

ImportError at /admin/
cannot import name patterns
...

おそらくこのチュートリアルのバージョンとは違う Django のバージョンを 使用していることでしょう。古いチュートリアルを参照するか、新しいバー ジョンの Django を入手してください。

管理サイトに入る

さあログインしてみましょう。(チュートリアルその 1 で、スーパユーザのアカウ ントを作成したはずです。覚えていますか?) ログインしたら、管理サイトのインデ クスページが表示されるはずです:

Django 管理サイトのインデクスページ

「グループ (Groups)」や「ユーザ (Users)」、「サイト (Sites)」といった編集 可能なコンテンツが表示されるはずです。これらはデフォルトで Django に付属し ているコアの機能です。

Poll モデルを admin 上で編集できるようにする

ところで、 polls アプリケーションはどこにあるんでしょう? admin のインデ クスページを見ても表示されていませんね。

実は、まだ一つやるべきことが残っていました。 admin に Poll モデルがイ ンタフェースを持つことを教える必要があるのです。 polls ディレクトリに admin.py ファイルを追加して、以下のように編集してください:

from polls.models import Poll
from django.contrib import admin

admin.site.register(Poll)

admin のページをリロードして、どんな変化が起きたか見てみましょう。通常、 開発サーバはプロジェクトを自動的にリロードしてくれるので、コードに加えた変 更はただちにブラウザで確認できます。

admin の機能を探究してみる

Poll モデルを登録したので、 Django は Poll を管理サイトに表示するよ うになりました:

Django 管理サイトに Poll が表示されるようになった

「Polls」 をクリックしてみてください。 チェンジリスト (change list) のペー ジに入ります。このページはデータベース上の全ての Poll オブジェクトを表 示していて、変更したいオブジェクトを選択できます。前のチュートリアルで作成 した 「What’s up」という Poll オブジェクトがありますね。

Poll のチェンジリストページ

「What’s up?」をクリックして編集してみましょう:

Poll オブジェクトの編集

以下の点に注意してください:

  • フォームは Poll モデルをもとに自動的に生成されています。
  • モデルのフィールドの型によって ( DateTimeField, CharField などの) 適切な HTML 入力ウィジェッ トが対応しています。各タイプのフィールドには、 Django 管理サイトでデー タを表示する方法が定義されています。
  • DateTimeField には JavaScript のショートカッ トがついています。日付 (Date) のカラムには「今日 (Today)」へのショート カットと、カレンダーのポップアップボタンがあります。時刻 (Time) のカラ ムには「現在 (Now)」へのショートカットと、よく入力される時間のリストを 表示するポップアップボタンがあります。

ページの末尾の部分には操作ボタンがいくつか表示されています:

  • 保存 (Save) – 変更を保存して、このモデルの変更リストのページに戻ります。
  • 保存して編集を続ける (Save and continue editing) – 変更を保存して、こ のオブジェクトの編集ページをリロードします。
  • 保存してもう一つ追加 (Save and add another) – 変更を保存して、このモデ ルのオブジェクトを新規追加するための空の編集ページをロードします。
  • 削除 (Delete) – 削除確認ページを表示します。

もし「Date published」の値がチュートリアル 1 で作成した時間と違う場合は、 TIME_SONE に現在のタイムゾーンの設定をし忘れた可能性があります。 変更し、リロードして正しい値が表示されるか確認してください。

「今日」や「現在」ショートカットをクリックして、「Date published」を変更し てみましょう。変更したら、「保存して編集を続ける」を押します。次に、右上に ある「履歴 (History)」をクリックしてみましょう。ユーザが管理サイト上でオブ ジェクトに対して行った全ての変更履歴を、変更時刻と変更を行ったユーザの名前 つきでリストにしたページを表示します:

Poll オブジェクトの変更履歴

管理サイトフォームのカスタマイズ

しばらく操作してみましょう。これだけの機能をコードを書かずに実現したこ とに驚くはずです。 admin.site.register(Poll) の呼び出しによって、 Django はオブジェクトの表示方法を「推測」し、管理サイトでモデルの編集を行え るようにします。管理サイトの表示や挙動を少し変えたい場合には、モデルを登録 するときにオプションを指定します。

試しに、編集フォームでのフィールドの並び順を並べ替えてみましょう。 admin.site.register(Poll) の行を以下のように置き換えてみてください:

class PollAdmin(admin.ModelAdmin):
    fields = ['pub_date', 'question']

admin.site.register(Poll, PollAdmin)

このように、 admin のオプションを変更したいときには、モデルごとに admin オブジェクトを生成して、 admin.site.register() の 2 番目の引数に渡すと いうパターンを使ってください。

上の例では、「Publication date」フィールドの表示位置を「Question」フィール ドよりも前に変更しています。

フィールドの並び順を変えた場合

二つしかフィールドがないので、あまりぱっとした変化ではありませんね。しかし admin フォームで何ダースものフィールドを操作するような場合には、直感的なフィー ルドの並び順というものはユーザビリティ上重要な要素です。

同じく何ダースもフィールドがある場合、フォームを複数のフィールドセットに分 割したいこともあるでしょう:

class PollAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question']}),
        ('Date information', {'fields': ['pub_date']}),
    ]

admin.site.register(Poll, PollAdmin)

fieldsets の各タプルの先頭の要素はフィールドセットのタイトルです。 フォームは以下のように表示されます:

フィールドセットでフォームを表示

各フィールドセットには任意の HTML クラスを指定できます。 Django では "collapse" というクラスを提供していますが、このクラスを指定すると、フィー ルドセットは最初折り畳まれた状態で表示されます。これは普段は使わないフィー ルドがたくさんあるようなフォームを使っている場合に便利です:

class PollAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question']}),
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]
フィールドセットが最初は折畳みで表示されている

リレーションを張ったオブジェクトの追加

OK、 Poll の管理サイトページはできました。しかし Poll は複数の Choice を持つのに、管理サイトページには表示されていませんね。

今のところは。

この問題の解決法は二つあります。一つ目は、 Poll と同様、以下のようにし

Choice モデルを管理サイトに登録するというものです:

from polls.models import Choice

admin.site.register(Choice)

これで、 Django の管理サイト上で「Choice」 を選べます。「Choice の追加」フォーム は以下のようになります:

管理サイトでの Choice の表示

このフォームでは「Poll」フィールドは選択ボックスで、データベース上の全ての Poll オブジェクトを選ます。 Django は ForeignKey を表示する時には <select> ボックスを使わねばならないということを知って いるのです。今の時点では、 Poll はデータベース上に一つしかないはずですね。

Poll フィールドの隣に「もう一つ追加 (Add Another)」リンクがあるのに注意して ください。 ForeignKey の関係にあるオブジェクトなら、何もしなくてもこのリン クが表示されます。「もう一つ追加」をクリックすると、「Poll を追加 (Add Poll)」というポップアップウィンドウを表示します。このウィンドウで Poll を追 加して「保存」を押すと、 Django は Poll をデータベースに保存して、もとの 「Choice の追加」フォームに選択済みの項目として動的に追加します。

しかし、この方法は Choice オブジェクトをシステムに追加するには効率的ではあ りません。むしろ、 Poll オブジェクトを追加する時に Choice をひと揃い追加出 来た方が便利ですよね。そうしてみましょう。

Choice モデルに対する register() を削除して、 Poll を登録する部分を 以下のように書き換えてください:

class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3

class PollAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question']}),
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]
    inlines = [ChoiceInline]

admin.site.register(Poll, PollAdmin)

この行は Django に対して、「Choice オブジェクトは Poll の管理ページから編集 する。デフォルトでは、 3 つの Choice を表示するのに十分なフィールドを用意す ること」と指示しています。

「Poll を追加」ページをロードして、どんな表示になったか見てみましょう。開発 サーバをリスタートする必要があるかもしれません:

「Poll を追加」ページに選択肢が表示された

変わった点をみてみましょう。リレーション相手である Choice を表示するために 3 つのスロットがあります (extra に指定した数ですね)。また、作成済みのオ ブジェクトを「編集する」ページに行けば、いつでも 3 つ余分のスロットが表示さ れるはずです。

さて、このコードにはちょっと問題があります。というのも、 Choice オブジェク トを入力するためのフィールドを全部表示しようとすると、相当な広さのスクリー ンが必要だからです。そこで、 Django にはテーブルを使ってインラインでリレー ション相手のオブジェクトを表示するもう一つの方法があります。以下のように、 ChoiceInline の宣言を変更してください:

class ChoiceInline(admin.TabularInline):
    #...

StackedInline に変わって TabularInline を使うと、 リレーション相手のオブジェクトはよりコンパクトなテーブル形式で表示されます:

「Poll を追加」ページの選択肢がコンパクトになった

管理サイトのチェンジリストページをカスタマイズする

さあ、これで Poll の管理ページはだいぶよくなってきました。今度は「チェンジ リスト」ページをすこしいじりましょう。チェンジリストは、システム上の全ての Poll を表示するページです。

作業前のチェンジリストのページは以下のように表示されています:

Poll のチェンジリストページ

デフォルトでは、 Django はオブジェクトの str() を表示しますが、各フィー ルドの値も表示されていると便利でしょう。表示させるには list_display オ プションを使います。 このオプションには、カラム表示したいフィールドの名前を タプルにして指定します:

class PollAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question', 'pub_date')

おまけとして、チュートリアル 1 で定義したカスタムメソッド was_published_recently も追加してみましょう:

class PollAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question', 'pub_date', 'was_published_recently')

これで、 Poll のチェンジリストのページは以下のようになります:

カスタムメソッドのカラムを追加した Poll のチェンジリスト

カラムのヘッダをクリックすると、カラムの値に応じてエントリを並べ換えできま す。ただし was_published_recently ヘッダは例外で、これはメソッドの戻り値を 使った並べ換えをサポートしていないからです。 was_published_recently のカラ ムヘッダのデフォルト値がメソッドの名前になっている (アンダースコアは空白に 置き換わっている)こと、各行が戻り値の文字列となっていることにも注意して下さい。

以下の属性を (models.py の中の) メソッドに与えることで、これを改良出来ま す:

class Poll(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'

admin.py ファイルを編集し、もう一点、 Poll のチェンジリストを改良して、フィル タをを加えます。以下の行を PollAdmin の中に入れます:

list_filter = ['pub_date']

これで、「フィルタ (Filter)」サイドバーができ、チェンジリストを pub_date フィールドの値に従ってフィルタできるようになります:

Poll のチェンジリストをさらに改良

表示されるフィルタのタイプは、フィルタに使うフィールドのタイプによって変わ ります。 pub_dateDateTimeField なので、 Django はデフォルトのフィルタのオプションとして 「すべての日 (Any date)」、「今日 (Today)」、「過去 7 日間 (Past 7 days)」、 「今月 (This month)」そして「今年 (This year)」を与えればよいと考えます。

細工は隆々ですね。こんどは検索機能を追加してみましょう:

search_fields = ['question']

これで変更リストの上部に検索ボックスが表示されます。ユーザが検索語を入力 すると、 Django は question フィールドを検索します。フィールドはいくつ でも使えますが、舞台裏では LIKE クエリを使っているので、データベースに 負荷をかけないためには常識的な検索語を指定しましょう。

最後に、 Poll オブジェクトには日付データがあるので、日付を使って絞り込める と便利なはずです。以下の一行を追加しましょう:

date_hierarchy = 'pub_date'

これで、日付を使った階層的なナビゲーションが変更リストページの上部に追加さ れます。トップレベルでは、エントリのある年を表示します。その下は月になって いて、最後は日ごとにエントリを表示します。

さて、変更リストには何もしなくてもページ分割機能がある、ということをここで お知らせしておいた方がよいでしょう。デフォルトではページあたり 100 個の要素 を表示します。ページ分割、検索ボックス、フィルタ、日付による階層化、カラム ヘッダを使った並べ換えといった変更リストの機能は、すべてが協調して思いのま まに動作するのです。

管理サイトのルック & フィールをカスタマイズする

管理サイトの上部には「Django 管理 (Django adminstration)」と表示されてい ますが、これはいささか滑稽ですね。これは単なるプレースホルダテキストにすぎ ません。

変更するのは簡単で、 Django のテンプレートシステムを使います。 Django の 管理サイトは、それ自身 Django で作られているので、インタフェースは Django のテンプレートシステムを使っているのです。

設定ファイル (mysite/settings.py でしたよね) を開いて、 TEMPLATE_DIRS という設定を探して下さい。 TEMPLATE_DIRS はファイルシステム上のディレクトリ名からなるタプル で、 Django テンプレートをロードするときに探す場所を指定します。つまり検索 パスです。

デフォルトでは、 TEMPLATE_DIRS には何も指定されていません。一行 追加して、Django に自作テンプレートの置き場所を教えましょう:

TEMPLATE_DIRS = (
    '/home/my_username/mytemplates', # 自分の環境に合わせて変更してください。
)

Django のソースコードの中にある admin アプリケーションのデフォルトのテンプ レート置場 (django/contrib/admin/templates) から、 admin/base_site.html というテンプレートをコピーして、 TEMPLATE_DIRS 上の admin というディレクトリ下に置きます。例え ば、 TEMPLATE_DIRS"/home/my_username/mytemplates" と設定 していれば、 django/contrib/admin/templates/admin/base_site.html/home/my_username/mytemplates/admin/base_site.html にコピーします。 admin というサブディレクトリを作るのを忘れないようにして下さい。

ファイルを編集して、 Django と書かれた部分を自分のサイトに合わせて変更して ください。

テンプレートファイルは、 {% block branding %}{{ title }} といっ たテキストを多く含みます。 {%{{ タグは Django のテンプレート言語 のうちの一つです。 Django が admin/base_site.html を読み込んだ時、この テンプレート言語が評価され、最終的な HTML ページが作られます。今の時点でテ ンプレートについて理解できなくても心配しないでください。テンプレート言語に ついてはチュートリアルその 3 で詳しく掘り下げていきます。

admin アプリケーションのデフォルトのテンプレートはどれもオーバライド可能で す。テンプレートをオーバライドするには、 base_site.html と同じ、つまり デフォルトのディレクトリからカスタムディレクトリにコピーして編集するという 手順をとってください。

賢明な読者はこう疑問に思うでしょう:「 TEMPLATE_DIRS はデフォルト で何も指定していないのに、 Django はどうしてデフォルトのテンプレートを捜し 当てられるのだろう?」答えは、「デフォルトでは、 Django はテンプレートが見 つからない場合、自動的に各アプリケーションパッケージの templates/ サブ ディレクトリ下を探すようフォールバックるようになっている」です。詳しくは テンプレートローダタイプの解説 を参照してください。

管理サイトインデクスページのカスタマイズ

もしかすると、同じようにして Django admin サイトのインデクスページのルック & フィールをカスタマイズしたくなるかもしれませんね。

デフォルトでは、インデクスページは INSTALLED_APPS 設定に登録され ていて、 admin アプリケーションに登録されている全てのアプリケーションをアル ファベット順に表示します。レイアウトはどのようにも変更できます。なんにせよ、 インデクスページというものは admin で最も重要なページであり、簡単に使えなけ ればならないはずです。

カスタマイズすべきテンプレートは admin/index.html です (前節の admin/base_site.html の場合と同じように、デフォルトのディレクトリからカ スタムテンプレート置場のディレクトリにコピーしてください)。ファイルを開くと、 app_list というテンプレート変数が見つかるはずです。この変数に、インス トール済みの全てのアプリケーションが入っています。この変数の代りに、特定の オブジェクトごとに admin ページのリンクをハードコードすれば、自分の思い通り に変更できます。繰り返しますが、もしテンプレート言語について理解できなくて も心配しないで下さい。チュートリアルその 3 でテンプレート言語について扱って いきます。

admin サイトを使いこなせるようになったら、 チュートリアルその 3 に進んで、 poll アプリケーショ ンの公開用ビュー作成にとりかかりましょう。

はじめての Django アプリ作成、その 3

revision-up-to:17812 (1.4)

このチュートリアルは チュートリアルその 2 の続き です。ここでは、引続きWeb 投票アプリケーションの開発を例にして、公開用のイ ンタフェース、ビュー(view) の作成を焦点に解説します。

ビューの設計哲学

ビューとは、 Django のアプリケーションにおいて特定の機能を提供するウェブペー ジの「型 (type)」であり、独自のテンプレートを持っています。例えばブログアプ リケーションなら、以下のようなビューがあるでしょう:

  • Blog ホームページ – 最新のエントリをいくつか表示します。
  • エントリの「詳細」ページ – 一つのエントリへの恒久リンク (permalink) ページです。
  • 年ごとのアーカイブページ – エントリのある年を表示します。
  • 月ごとのアーカイブページ – ある年のエントリのある月を表示します。
  • 日ごとのアーカイブページ – ある月のエントリのある日を表示します。
  • コメント投稿 – あるエントリに対するコメントの投稿を受け付けます。

Poll アプリケーションの場合には、以下の 4 つのビューを作成します:

  • Poll の「アーカイブ」ページ – 最新の調査項目 (poll) をいくつか表示し ます。
  • Poll の「詳細」ページ – 調査項目と投票用フォームを表示します。開票結 果は表示しません。
  • Poll の「開票結果」ページ – ある調査項目に対する結果を表示します。
  • 投票ページ – ある調査項目のある選択肢に対する投票を受け付けます。

Django では、各ビューは簡単な Python の関数として表現されます。

URL スキームの設計

ビューを書く上での最初のステップは、 URL 構造の設計です。 URL 構造を定義す るには、 URLconf と呼ばれる Python モジュールを作成します。 Django は URLconf を使ってどの URL をどの Python コードに関連づけるかを決めています。

ユーザが Django で作られたページをリクエストすると、システムは ROOT_URLCONF 設定を探します。この設定には Python モジュールをドッ ト表記で表した文字列が入っています。 Django は指定されたモジュールをロード して、 urlpatterns という名前のモジュールレベル変数を探します。 urlpatterns は以下のような形式のタプルからなるシーケンスです:

(正規表現, Pythonのコールバック関数,  [, オプションの辞書オブジェクト])

Django は先頭のタプルから順に、リクエストされた URL とタプル内の正規表現が マッチするまでテストしてゆきます。

マッチする正規表現が見つかると、Django は該当するタプルに指定されているコー ルバック関数を呼び出します。コールバック関数には HttpRequest オブジェクトを第一引数として渡します。 さらに、正規表現内で「キャプチャした (captured)」値をキーワード引数として渡 します。(タプルの三番目の要素である) オプションの辞書オブジェクトが指定され ていれば、その内容も追加のキーワード引数として渡します。

HttpRequest` オブジェクトの詳細は リクエストオブジェクトとレスポンスオブジェクト を参照してください。 URLconf の詳細は URL ディスパッチャ を参照してください。

チュートリアルその 1 の冒頭で python django-admin.py startproject mysite を実行していれば、URLconf が mysite/urls.py にできているはずです。また、(settings.py の) ROOT_URLCONF 設定にも、自動的に値が入ります:

ROOT_URLCONF = 'mysite.urls'

さて、例題を使って練習する時が来ました。 mysite/urls.py を編集して、 以下のような内容にしましょう:

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^polls/$', 'polls.views.index'),
    url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
    url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
    url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
    url(r'^admin/', include(admin.site.urls)),
)

復習しておいたほうがよいですね。だれかが Web サイトのあるページ、例えば “/polls/23” をリクエストすると、 Django は ROOT_URLCONF に書かれている この Pythonモジュールをロードします。Django はモジュール内の urlpatterns という変数を探し、その中に入っている正規表現を順に検査して ゆきます。マッチする正規表現が見つかった (r'^polls/(?P<poll_id>\d+)/$' ですね) ところで、 Django はこの正規表現に関連づけられている Python パッケー ジ/モジュールである detail()polls/views.py からロードし ます。これは最後に、 Django は以下のような引数で detail() を呼び出し ます:

detail(request=<HttpRequest object>, poll_id='23')

poll_id='23' の部分は、 (?P<poll_id>\d+) からきています。パターンを 丸括弧で囲うと、パターンにマッチしたテキストを「キャプチャ」して、ビュー関 数の引数として送り込みます。 ?P<poll_id> はマッチしたパターンを識別する ための名前をつけています。 \d+ は数字の列 (すなわち番号) にマッチする正 規表現です。

URL パターンは正規表現なので、正規表現で実現できる限り無制限の URL を表現で きます。また、 .php のようなくだらない文字列を URL に追加する必要もあり ません。ただし、病的なユーモアの持ち主のために、以下のようにすれば実現でき ることは示しておきましょう:

(r'^polls/latest\.php$', 'polls.views.index'),

とはいえ、こんな阿呆なことはやめましょう。

これらの正規表現では GET や POST のパラメタ、またドメイン名を検索しないこと に注意してください。例えば、 http://www.example.com/myapp/ というリクエ ストが来ると、 URLconf は /myapp/ を探します。 http://www.example.com/myapp/?page=3 の場合にも、 URLconf は /myapp/ を探します。

正規表現をよく理解できないなら、 Wikipedia のエントリre モジュ ールのドキュメントを参照してください。また、オライリーから出版されている本、 「詳説 正規表現」 (Jeffrey Friedl 著、 訳注: 田和 勝 訳) に秀逸な解説が あります。

最後に、パフォーマンスについての注意です: 正規表現は URLconf モジュールをロー ドする時にコンパイルされ、極めて高速に動作します。

はじめてのビュー作成

さてと、まだビューを作っていませんね。まだ URLconf しかありません。しかし、 まずは Django が URLconf を正しく理解できているか調べておきましょう。

Django 開発サーバを起動してください:

python manage.py runserver

Web ブラウザで “http://localhost:8000/polls/” に行ってみましょう。以下のよ うなメッセージの入った綺麗に彩られたエラーページが表示されるはずです:

ViewDoesNotExist at /polls/

Could not import polls.views.index. View does not exist in module
polls.views.

このエラーは、まだ index() という関数を polls/views.py に書いていな いために発生しています。

“/polls/23/” や “/polls/23/results/” 、 “/polls/23/vote/” も試して下さい。 エラーメッセージから、 Django がどのビューを試した (そしてまだビューを書い ていないので失敗した) かがわかります。

いよいよ最初のビューを書きましょう。 polls/views.py というファイ ルを開いて、以下のような Python コードを書いてください:

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the poll index.")

最も単純なビューです。ブラウザで “/polls/” にアクセスすると、テキストが表示 されるはずです。

もう少しビューを追加しましょう。これらのビューはさきほどと少し違って、引数 をとります (この引数には、URLconf の正規表現内でキャプチャされたものが渡さ れます):

def detail(request, poll_id):
    return HttpResponse("You're looking at poll %s." % poll_id)

def results(request, poll_id):
    return HttpResponse("You're looking at the results of poll %s." % poll_id)

def vote(request, poll_id):
    return HttpResponse("You're voting on poll %s." % poll_id)

ブラウザを使って “/polls/34/” を見てください。これは detail() メソッドを 呼びだし、 URL に指定した ID を表示します。 “/polls/34/results/” と “/polls/34/vote/” を見てください。それぞれ開票結果ページと投票ページを表示 します。

実際に動くビューを作成する

各ビューは、リクエストされたページのコンテンツが入った HttpResponse オブジェクトを返すか、 Http404 のような例外を送出するかの 2 つの動作のうち、い ずれかを実行せねばなりません。それ以外の処理は全てユーザにゆだねられていま す。

ビューはデータベースからレコードを読みだしても、読み出さなくてもかまいませ ん。 Django のテンプレートシステム (あるいはサードパーティの Python テンプ レートシステム) を使ってもよいですし、使わなくてもかまいません。 PDF ファイ ルを生成しても、 XML を出力しても、 ZIP ファイルをオンザフライで生成しても かまいません。 Python ライブラリを使ってやりたいことを何でも実現できます。

Django にとって必要なのは HttpResponse か、あるいは 例外です。

簡単のため、 チュートリアルその 1 で解説した Django のデータベース API を使ってみましょう。 index() ビューを、システ ム上にある最新の 5 件の質問項目をカンマで区切り、日付順に表示させてみます:

from polls.models import Poll
from django.http import HttpResponse

def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    output = ', '.join([p.question for p in latest_poll_list])
    return HttpResponse(output)

残念ながらこのコードには問題があります。ページのデザインがビュー中にハード コードされているのです。これでは、ページの見栄えを変えたくなるたびに Python コードをいじらねばなりません。というわけで、 Django のテンプレートシステム を使って、デザインと Python コードを分離しましょう:

from django.template import Context, loader
from polls.models import Poll
from django.http import HttpResponse

def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    t = loader.get_template('polls/index.html')
    c = Context({
        'latest_poll_list': latest_poll_list,
    })
    return HttpResponse(t.render(c))

このコードは “polls/index.html” とい名前のテンプレートをロードし、テンプレー トにコンテキストを渡します。コンテキストとは、テンプレート変数名を Python のオブジェクトに対応づけている辞書です。

ページをリロードしましょう。エラーが出るようになったはずです:

TemplateDoesNotExist at /polls/
polls/index.html

そうそう、まだテンプレートを作ってませんね。まず、ファイルシステム上の Django がアクセス出来る場所にディレクトリを作成します (Django はサーバを実 行しているユーザと同じユーザ権限で動作します)。ただし、Web サーバのドキュメ ントルートには置かないようにしましょう。セキュリティへの配慮として、テンプ レートを公開するべきではないと思います。次に、 settings.pyTEMPLATE_DIRS を編集して、どこでテンプレートを探せばよいか Django に教えます。チュートリアルその 2 の「admin サイトのルック & フィール をカスタマイズする」でやったのと同じ作業です。

設定がおわったら、テンプレートディレクトリに polls というディレクトリを 作成します。このディレクトリの中に、 index.html という名前のファイルを 作成してください。 loader.get_template('polls/index.html') というコード は、ファイルシステム上の “[template_directory]/polls/index.html” に対応する ことに注意して下さい。

テンプレートには以下のようなコードを書きます:

{% if latest_poll_list %}
    <ul>
    {% for poll in latest_poll_list %}
        <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

ブラウザでページをロードすると、チュートリアル 1 で作った “What’s up” とい う投票項目の入ったブレットリストを表示します。リンクは詳細ページを指します。

ショートカット: render_to_response()

テンプレートをロードしてコンテキストに値を入れ、テンプレートをレンダリング した結果を HttpResponse オブジェクトで返す、というイ ディオムは非常によく使われます。 Django はこのイディオムのためのショートカッ トを提供しています。ショートカットを使って index() ビューを書き換えてみ ましょう:

from django.shortcuts import render_to_response
from polls.models import Poll

def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    return render_to_response('polls/index.html',
                              {'latest_poll_list': latest_poll_list})

この作業によって、 loaderContextHttpResponse を import する必要はなくなりました。

render_to_response() 関数は第一引数にテンプレートの 名前をとり、オプションの第二引数に辞書をとります。 render_to_response() はテンプレートを指定のコンテキ ストでレンダリングし、 HttpResponse オブジェクトにし て返します。

404 の送出

さて、今度は Poll の詳細ページを片付けましょう。このページは Poll の質問文 (question フィールド) を表示します。ビューは以下のようになります:

from django.http import Http404
# ...
def detail(request, poll_id):
    try:
        p = Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404
    return render_to_response('polls/detail.html', {'poll': p})

新しい概念がでて来ました。このビューはリクエストした ID を持つ Poll が存在 しないときに Http404 を送出します。

polls/detail.html テンプレートに何を書けばよいかは後で解説しますが、さ しあたって上の例題を動かしたければ、単に:

{{ poll }}

と書いておいてください。

ショートカット: get_object_or_404()

get() を実行し、該当オブジェクトがな い場合には Http404 を返す、という作業は非常によく使われ るイディオムです。 Django はこのイディオムのためのショートカットを提供してい ます。ショートカットを使って detail() ビューを書き換えてみましょう:

from django.shortcuts import render_to_response, get_object_or_404
# ...
def detail(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/detail.html', {'poll': p})

get_object_or_404() は Django のモデルクラスを第一 引数にとり、任意の個数のキーワード引数をとります。キーワード引数はそのまま get() メソッドに渡ります。オブジェク トが存在しなければ Http404 を送出します。

設計哲学

なぜ ObjectDoesNotExist 例外を高水準で自 動的にキャッチせず、ヘルパー関数 get_object_or_404() を使うのでしょうか、また、 なぜモデルAPI に ObjectDoesNotExist では なく Http404 を送出させるのでしょうか?

答えは、「モデルレイヤとビューレイヤをカップリングしてしまうから」です。 Django の最も大きな目標の一つは、ルーズカップリングの維持にあります。

get_list_or_404() という関数もあります。この関数は get_object_or_404() と同じように働きますが、 get() ではなく filter() を使います。リストが空の 場合は Http404 を送出します。

404 応答 (ページを見つけられません) 用のビューを作る

ビュー内で Http404 を送出すると、 Django は 404 エラー 処理用の特殊なビューをロードします。このビューは URLconf にある handler404 という変数 ( URLconf にあるもののみ有効です。その他に設定さ れたものは影響がありません) を参照して見つけます。変数は URLconf のコール バックで使っているのと同じ、 Python のドット表記法で表した関数名の文字列で す。 404 ビュー自体に特殊なところはありません。単なる普通のビューです。

普通はわざわざ苦労して 404 ビューを書く必要はありません。もし handler404 を設定しなかった場合は、ビルトインビューである django.views.defaults.page_not_found() がデフォルトで使用されます。 この場合、 404.html テンプレートをテンプレートディレクトリのルートに 作成する必要があります。デフォルトの 404 ビューはこのテンプレートを全ての 404 エラーに対して使用します。もし (設定モジュールで) DEBUGFalse に設定していて、なおかつ 404.html を作成しなかった場合、 Http500 が代わりに送出されます。 404.html を作成することを覚えて おいてください。

404 ビューに関するいくつかの注意すべき点:

  • (設定モジュールで) DEBUGTrue に設定している場合、 Django は 404 ビューを使わず、トレースバックを表示します (404.html も使われません)。
  • 404 ビューは、 Django が URLconf の全ての正規表現をチェックして、一致 するものがなかった場合にも呼び出されます。

500 応答 (サーバエラー) 用のビューを作る

404 と同じように、ルートの URLconf で handler500 を定義できます。 handler500 はサーバエラーが生じたときに呼び出されるビューを指します。 サーバエラーになるのは、ビューコードに実行時エラーが生じた場合です。

テンプレートシステムを使う

detail() ビューに戻りましょう。コンテキスト変数を poll とすると、 polls/detail.html テンプレートは以下のように書けます:

<h1>{{ poll.question }}</h1>
<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice }}</li>
{% endfor %}
</ul>

テンプレートシステムはドットを使った表記法で変数の属性にアクセスします。 {{ poll.question }} を例にとると、まず Django は poll オブジェクト を辞書とみなして question の値を探します。これには失敗するので、今度は 属性として照合を行い、この場合は成功します。仮に属性の照合に失敗すると、 Django は リストインデックスでの照合を行おうとします。

メソッド呼び出しは {% for %} ループの中で行われています。 poll.choice_set.all は、 Python のコード poll.choice_set.all() に解 釈されます。その結果、 Choice オブジェクトからなるイテレーション可能オブジェ クトを返し {% for %} タグで使えるようになります。

テンプレートの詳しい動作は テンプレートガイド を 参照してください。

URLconf の単純化

しばらくビューとテンプレートシステムをいじってみてください。 URLconf を編集 していくうちに、実はかなり冗長な部分があることに気づくかもしれません:

urlpatterns = patterns('',
    url(r'^polls/$', 'polls.views.index'),
    url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
    url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
    url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
)

明らかに、どのコールバックにも polls.views が入っています。

これはよくあることなので、 URLconf フレームワークではプレフィクスを共有する ためのショートカットがあります。共通のプレフィクスを切り出して、以下のよう に patterns() の最初の引数に追加してくださ い:

urlpatterns = patterns('polls.views',
    url(r'^polls/$', 'index'),
    url(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
    url(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
)

機能的には前の形式と全く同じです。でもとてもすっきりしましたね。

普通、一つのアプリに対してのプレフィクスを URLconf にある全てのコールバック に適用させたくはないので、複数の patterns() を結合 させることが出来ます。 mysite/urls.py は以下のようになるでしょう:

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('polls.views',
    url(r'^polls/$', 'index'),
    url(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
    url(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
)

urlpatterns += patterns('',
    url(r'^admin/', include(admin.site.urls)),
)

URLconf の脱カップリング

ところで、すこし時間を割いて polls アプリケーションの URL 設定を Django プ ロジェクトの設定から切り離しましょう。 Django アプリケーションはプラグ可能、 つまりあれこれ設定しなくても、特定のアプリケーションを別の Django インストー ルに移動できるようになっています。

今のところ、 python manage.py startapp で作成した厳密なディレクトリ構成 のおかげで polls アプリケーションはかなりうまく脱カップリングできていますが、 一点だけ現在の Django の設定とカップリングしている場所があります: それは URLconf です。

これまでは URL 設定を mysite/urls.py で編集してきましたが、本来アプリケー ションの URL 設計はアプリケーション固有のものであって、特定の Django インス トールとは関係のないものです。そこで、 URL 設定をアプリケーションディレクト リ内にもってきましょう。

mysite/urls.py ファイルを polls/urls.py にコピーしてください。 次に mysite/urls.py から polls に関する URL 設定を全て削除し、以下の include() を一つだけ書くと、以下が残ります:

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^polls/', include('polls.urls')),
    url(r'^admin/', include(admin.site.urls)),
)

簡単に説明すると、 include`() は別の URLconf への 参照です。正規表現に $ (文字列の末尾にマッチする) が付いておらず、代り にスラッシュがついていることに注意してください。 Django は ~django.conf.urls.include を見つけると、リクエスト URL 中のマッ チした部分を切り離し、残りの部分を include されている URLconf に送って処理 させます。

従って、このシステムでユーザが “/polls/34/” にアクセスすると、次のように処 理されます:

  • Django は '^polls/' へのマッチを検出します。
  • Django はマッチ部分のテキスト ("polls/") を取り去り、残りのテキスト である "34/" を ‘polls.urls’ という URLconf に送り、処理させ ます。

これでプロジェクト側の脱カップリングはできました。今度は polls.urls 側を脱カップリングするために、 URLconf の各行から、 先頭の “polls/” を削除してください。 polls/urls.py ファイルは以下のよう になります:

from django.conf.urls import patterns, include, url

urlpatterns = patterns('polls.views',
    url(r'^$', 'index'),
    url(r'^(?P<poll_id>\d+)/$', 'detail'),
    url(r'^(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)

include() と URLconf の脱カップリングの背景には、 URL のプラグ & プレイを簡単にしようという発想があります。今や polls は独自の URLconf を持っているので、 “/polls/” や “/fun_polls/”, “/content/polls/” といった、どんな パスルート下にも置けて、どこに置いてもきちんと動作します。

polls アプリケーションは絶対パスではなく相対パスだけに注意しているわけです。

ビューの作成に慣れたら、 チュートリアルその 4 に 進んで、簡単なフォーム処理と汎用ビュー (generic view) の使い方を学びましょ う。

はじめての Django アプリ作成、その 4

revision-up-to:17812 (1.4)

このチュートリアルは チュートリアルその 3 の続き です。ここでは、引続き Web 投票アプリケーションの開発を例にして、簡単なフォー ム処理とコードの縮小化を中心に解説します。

簡単なフォームを書く

それでは、前回のチュートリアルで作成した Poll の詳細ビュー用テンプレート ("polls/detail.html") を更新して、 HTML <form> エレメントを入れてみ ましょう:

<h1>{{ poll.question }}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<form action="/polls/{{ poll.id }}/vote/" method="post">
{% csrf_token %}
{% for choice in poll.choice_set.all %}
    <input type="radio" name="choice" id="choice{{ forloop.counter }}"
     value="{{ choice.id }}" />
    <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
{% endfor %}
<input type="submit" value="投票する" />
</form>

簡単に説明しましょう:

  • 上のテンプレートでは、 Poll の選択肢ごとにラジオボタンを表示していま す。各ラジオボタンの value は Choice の ID に関連づけられています。 ラジオボタンの name はいずれも "choice" です。つまり、投票者 がラジオボタンのいずれかを選択してフォームを提出 (submit) すると、 choice=3 という内容のPOST データを送信します。これは HTML フォー ムの基本ですね。
  • フォームの action/polls/{{ poll.id }}/vote/ に設定し、 method="post" にしています。 (method="get" ではなく) method="post" を使っている点は極めて重要です。というのも、このフォー ムの提出はサーバ側のデータの更新につながるからです。サーバ側のデータ を更新するようなフォームを作成するときは、常に method="post" を使 いましょう。これは Django 固有の話ではなく、いわば Web 開発の王道です。
  • forloop.counter は、 for タグのループが何度実行されたかを 表す値です。
  • データが改ざんされる恐れのある POST のフォームを作成しているので、クロス サイトリクエストフォージェリ (Cross Site Request Forgeries) のことを心配 する必要があります。 ありがたいことに、 Django がこれに対応するとても使いやすい仕組みを提供し てくれているので、あまり心配する必要はありません。手短に言うと、全ての 自サイトへ向けての POST フォームに対しては {% csrf_token %} テンプレートタグを使いましょう。

{% csrf_token %} タグは、テンプレートコンテキストから はアクセスできないようなリクエストオブジェクトの情報を必要とします。このた めに、少し detail ビューに変更を加える必要があります。変更を加えた後は 以下のようになります:

from django.template import RequestContext
# ...
def detail(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/detail.html', {'poll': p},
                               context_instance=RequestContext(request))

これがどのように動くかについての詳細は、 RequestContext で詳しく説明し ています。

さあ、今度は提出されたデータを処理するための Django ビューを作成しましょう。 チュートリアルその 3 で、以下のような行を polls アプリケーションの URLconf に入れたことを思い出しましょう:

(r'^(?P<poll_id>\d+)/vote/$', 'vote'),

また、 vote() 関数のダミーの実装も作成しました。今度は本物を作成しま しょう。以下を polls/views.py に追加してください:

from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.template import RequestContext
from polls.models import Choice, Poll
#...
def vote(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    try:
        selected_choice = p.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # Poll 投票フォームを再表示します。
        return render_to_response('polls/detail.html', {
            'poll': p,
            'error_message': "選択肢を選んでいません。",
        }, context_instance=RequestContext(request))
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # ユーザが Back ボタンを押して同じフォームを提出するのを防ぐ
        # ため、POST データを処理できた場合には、必ず
        # HttpResponseRedirect を返すようにします。
        return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,)))

このコードには、これまでのチュートリアルで扱っていなかったことがいくつか 入っています:

  • request.POST は辞書ライクなオ ブジェクトです。このオブジェクトを使うと、キー名を使って入力されたデー タにアクセスできます。この例では、 request.POST['choice'] で投票者の選んだ選択肢を文字列で返させています。 request.POST に入っている値は 常に文字列です。

    Django では、 POST と同様、 GET データにアクセスするための request.GET も提供しています。 ただし、このコードでは、POST を経由した呼び出しでないとデータを更新さ せないようにするために、 request.POST を明示的に使って います。

  • choice が POST データ上になければ、 request.POST['choice']KeyError を送出します。上のコードでは KeyError をチェッ クして、 choice がない場合にはエラーメッセージ付きの Poll フォー ムを再表示しています。

  • choice のカウントを増やした後で、 HttpResponse ではなく HttpResponseRedirect を返しています。 HttpResponseRedirect はリダイレクト先の URL 一 つだけを引数にとります (ここでは reverse() を使って URL を生成していま すが、これについては後で説明します)。

    上のコードの Python コメント文で指摘しているように、 POST データの処 理に成功したときは常に HttpResponseRedirect を 返してください。これは Django 固有の話ではなく、 Web 開発の王道です。

  • 例では、 HttpResponseRedirect のコンストラクタ の中で reverse() という関数を使ってい ます。この関数を使うと、ビュー関数中での URL のハードコードを防げます。 reverse() にはビューの名前を渡し、同 時に URL パターンからビューにマップするときに取り出される変数を指定し ます。上の例では、 reverse()チュートリアルその 3 で設定した URLconfに従っ て:

    '/polls/3/results/'
    

    のような URL を返します。 3p.id の値です。リダイレクト先 の URL は 'results' ビューを呼び出し、最終的なページを表示します。 (プレフィクスを含めた) ビューの完全な名前を指定せねばならないので注意 してください。

チュートリアルその 3 で触れたように、 requestHTTPRequest オブジェクトです。 HTTPRequest の詳細は リクエスト・レスポンスオブジェクトのドキュメント を参照してください。

投票者が Poll に投票すると、 vote() ビューは開票結果ページにリダイレク トします。開票ページを書きましょう:

def results(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/results.html', {'poll': p})

テンプレート名が違うことだけを除き、 チュートリアルその 3detail() とほとんど同 じですね。この冗長さは後で修正することにします。

今度は results.html テンプレートを作成します:

<h1>{{ poll.question }}</h1>

<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice }} -- {{ choice.votes }}</li>
{% endfor %}
</ul>

<a href="/polls/{{ poll.id }}/">Vote again?</a>

さあ、ブラウザで /polls/1/ を表示して、投票してみましょう。票を入れるた びに、結果のページが更新されていることがわかるはずです。選択肢を選ばずにフォー ムを提出すると、エラーメッセージを表示するはずです。

汎用ビューを使う: コードが少ないのはいいことだ

チュートリアルその 3detail()results() という二つのビューはバカバカしいくらいに単純で、先程も述べた ように冗長です。(これまた チュートリアルその 3 の) Poll のリストを表示する index() ビューも同様です。

こうしたビューは、基本的な Web 開発においてよくあるケース。すなわち、URL を 介して渡されたパラメタに従ってデータベースからデータを取り出し、テンプレー トをロードして、レンダリングしたテンプレートを返す、というケースを体現して います。これはきわめてよくあるケースなので、 Django では「汎用ビュー (generic view)」というショートカットのシステムを提供しています。

汎用ビューとは、よくあるパターンを抽象化して、 Python コードすら書かずにア プリケーションを書き上げられる状態にしたものです。

これまで作成してきた polls アプリケーションを汎用ビューシステムに変換して、 コードをばっさり捨てられるようにしましょう。変換にはほんの数ステップしかか かりません。そのステップとは:

  1. URLconf を変換する。
  2. 不要になった古いビューを削除する。
  3. 新しいビュー用に URL のハンドリングを修正する。

です。詳しく見てゆきましょう。

なぜ今更コードを入れ換えるの?

一般に Django アプリケーションを書く場合は、まず自分の問題を解決するため に汎用ビューが適しているか考えた上で、最初から汎用ビューを使い、途中ま で書き上げたコードをリファクタすることはありません。ただ、このチュート リアルでは中核となるコンセプトに焦点を合わせるために、わざと「大変な」 ビューの作成に集中してもらったのです。

電卓を使う前に、算数の基本を知っておかねばならないのと同じです。

まず polls の URLconf である polls/urls.py を開きます。チュートリアルで のこれまでの作業から、中身は以下のようになっているはずです:

from django.conf.urls import patterns, include, url

urlpatterns = patterns('polls.views',
    url(r'^$', 'index'),
    url(r'^(?P<poll_id>\d+)/$', 'detail'),
    url(r'^(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)

これを以下のように変更しましょう:

from django.conf.urls import patterns, include, url
from django.views.generic import DetailView, ListView
from polls.models import Poll

urlpatterns = patterns('',
    url(r'^$',
        ListView.as_view(
            queryset=Poll.objects.order_by('-pub_date')[:5],
            context_object_name='latest_poll_list',
            template_name='polls/index.html')),
    url(r'^(?P<pk>\d+)/$',
        DetailView.as_view(
            model=Poll,
            template_name='polls/detail.html')),
    url(r'^(?P<pk>\d+)/results/$',
        DetailView.as_view(
            model=Poll,
            template_name='polls/results.html'),
        name='poll_results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
)

この例では二つの汎用ビュー、 ListViewDetailView を使っています。こ れらのビューはそれぞれ、「オブジェクトのリストを表示する」および「あるタイ プのオブジェクトの詳細ページを表示する」という二つの概念を抽象化しています。

  • 各汎用ビューは自分がどのモデルに対して動作するのか知っておく必要があ ります。これは model パラメタによって提供されます。
  • DetailView 汎用ビューには、 "pk" という名前で URL から プライマリキー をキャプチャ して渡すことになっています。そこで、汎用ビュー向けに poll_idpk に書き換えてあります。
  • 結果を表示するビューに poll_results という名前をつけてあります。 こうすると、このビューを呼び出すような URL を後で生成できます (詳しくは「 名前付きパターン <naming-url-patterns> 」の説明を参照し てください。)また、 django.conf.urls モジュールの url() 関数を使っています。上の例のように、 パターン名を指定する場合には、 url() を使う よう薦めます。

デフォルトでは、 DetailView 汎用 ビューは <app name>/<model name>_detail.html という名前のテンプレート を使います。私達のアプリケーションでは、テンプレートの名前は "polls/poll_detail.html" です。 template_name 引数は Django に自動生成されたデフォルトのテンプレート名 ではなく、指定した名前を使うように伝えるために使われます。また、 result リストビューにも template_name を指定します。これは結果 (result) ビューと詳細 (detail) ビューが、お互い実は DetailView であるにも関わらず、 レンダリングされたときに違った外観を持っているためです。

同様に、 ListView 汎用ビューも <app name>/<model name>_list.html という名前のテンプレートを使うので、 template_name を使って ListView に既存の polls/index.html テンプレートを使用するように伝えます。

このチュートリアルの前の部分では、 polllatest_poll_list といった変数の入ったコンテキスト (context) をテンプレートに渡していました。 DetailView には、 poll という変数が自動的に渡されます。なぜなら、今 私達は Django モデル (Poll) を使用していて、 Django はコンテキスト 変数にふさわしい名前を決めることができるからです。一方で、 ListView では、 自動的に生成されるコンテキスト変数は poll_list となります。これを上書 きするには、 context_object_name オプションを与えて、 latest_poll_list を代わりに使用すると指定します。この代替アプローチと して、新しいデフォルトのコンテキスト変数と一致するようにテンプレートを変 えることもできます。しかし、ただ Django に使用したい変数名を伝えるほうが 簡単でしょう。

さて、 index(), detail() および results() ビューのコードを polls/views.py から削除できるようになりました。これらのビュー関数は汎用 ビューで置き換わったので、もう必要ありません。

最後に、 URL が汎用ビューを指すように修正します。上の vote ビューでは、 reverse() 関数を使って URL のハードコードを 防いでいます。汎用ビューに切替えたので、 reverse() を変更して、URL が新しく追加した 汎用ビューを指すようにします。汎用ビューのビュー関数を使えれば簡単なのです が、汎用ビューというものは一つのサイトの中で何度も使われることがあるので、 そういうわけにはいかないのです。そこで、先程指定しておいたビューの名前を使 います:

return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))

サーバを実行して、新しく汎用ビューベースにした投票アプリケーションを使って みましょう。

汎用ビューの詳細は 汎用ビューのドキュメント を参照してく ださい。

次回予告

このチュートリアルはここでしばらく中断します。今後は以下のような内容をカバー する予定です:

  • 高度なフォーム処理
  • RSS フレームワークを使う
  • キャッシュフレームワークを使う
  • コメントフレームワークを使う
  • 管理サイトの高度な機能: パーミッション
  • 管理サイトの高度な機能:カスタム JavaScript

さしあたっては、 次のステップへ に進むとよいでしょ う。

次のステップへ

revision-up-to:17812 (1.4)

さあ、ここにきたあなたは、 入門編 をあらかた読み終えて、 Django を使い続ける決心をしたことだと思います。入門編では、 Django のほんの 表面的な部分に触れただけにすぎません (実際、入門編の分量は、全体のドキュメ ントの 10% にも満たないのです)。

さて、次は何をしましょうか?

そうですね、私達は実践を通して学ぶのがとても大好きですよね。今や、読者のみ なさんは自分のプロジェクトを作成して、いろいろと遊んでみるのに十分な段階に あります。新しい技を身に付けたければ、いつでもここに戻って来てください。

私達は、 Django ドキュメントを有意義で、読みやすく、可能な限り完璧にしよう と努力しています。このドキュメントの残りの部分では、読者の皆さんが Django のドキュメントをできるだけ活用できるように、ドキュメントがどういう仕組みに なっているのかを説明しています。

(そう、これはいわばドキュメントのドキュメントです。ただし、このドキュメント の読み方を説明するドキュメントを書いたりはしませんから心配しないでください ね。)

ドキュメントを探す

Django には、いまや 200,000 語を越す たくさん のドキュメントがあります。 その中から必要なものを捜し出すのは大変です。そういうときは、 Search PageIndex から辿るとよいでしょう。

もちろん、片っ端から読み進むのもいいですけどね。

ドキュメントの構成

Django のドキュメントは、用途に応じていくつかの部に分かれています:

  • 入門編 は、 Django や、ウェブ開発全般の初心者を 対象に作られています。あまり詳細な解説はありませんが、高い視点で Django の開発が「どのようなものか」を見られます。
  • トピック別ガイド では、 Django の個々の構成要素 について詳しく解説しています。このセクションでは、 Django の モデルシステム, テンプレートエンジン, フォームフレームワーク といったトピックを解説しています。

    おそらく、読者の皆さんはこのセクションを読み進むのに多くの時間を費す でしょう。このガイドを全て読破したら、 Django を扱う上で必要なことは ほとんど知っているはずです。

  • ウェブ開発に必要な知識は、いくつもの領域にまたがって広く、浅く分布し ているものです。 このセクションには、「〜をするにはどうしたらよいです か?」といった質問に答える、 HOWTO が書かれて います。例えば、 Django で PDF を生成する方法 や、 テンプレートタグを自作する方法 などです。

    よくある質問は、これとは別に FAQ で扱っています。

  • ガイドや HOWTO ドキュメントは、 Django の全てのクラスや関数、メソッ ドを解説しているわけではありません。 Django を学ぼうとする人に最初か ら全てを教えようとしても、溢れてしまうからです。その代わりに、個々の クラスや関数、メソッド、モジュールの解説を リファレンス に置きました。特定の関数の仕様や、どんな機能を使えるかを 調べたければ、このセクションを参照してください。

ドキュメント更新の方針

Django のコードベースが毎日のように開発と改良を重ねているように、ドキュメン トも常に改良を重ねています。ドキュメントの改良は以下のような理由に基づいて 行われます:

  • 文法やタイプミスなどの誤りを修正する場合。
  • 既存の内容に対して、新たに情報や例題を追加する場合。
  • まだ解説されていない Django の機能をドキュメント化する場合 (未ドキュ メントの機能は減りつつありますが、まだいくつか残っています)。
  • 新たな機能が追加され、ドキュメントも追加する場合。あるいは、 Django の API や挙動が変更された場合。

Django のドキュメントはコードと同じソースコード管理システム下にあり、 Subversion リポジトリの django/trunk/docs ディレクトリ以下に置かれていま す。各ドキュメントは、例えば「汎用ビュー」フレームワークや、データベースモ デルの構築方法といった具合に、個別のトピックごとに別々のテキストファイルに なっています。

ドキュメントの入手

Django のドキュメントを入手するにはいくつか方法があります。おすすめの順に以 下に示します:

Web 版

Django ドキュメントの最新版は http://docs.djangoproject.com/en/dev/ にあります。ここにある HTML ページは、ソースコード管理システム上のテキスト ファイルから自動生成されているものです。従って、これらのファイルは「最新最 良の」 Django に対応しています。つまり、最近の修正や追加事項を反映していて、 まだ開発版でしか使えないような最新の機能についても部分的に解説しているわけ です (後述の「バージョン間の相違点」を参照してください)。

ドキュメント改良のお手伝いは大歓迎です。変更すべき点、修正すべき点、改良す べき点などを チケットシステム に提出してください。Django の開発陣がチケッ トシステムを監視して、あなたのフィードバックが皆に恩恵をもたらすようにしま す。

ただし、チケットは一般的なテクニカルサポートに関わる質問ではなく、ドキュメ ント自体に関する内容にしてください。 Django のセットアップに関する個別の問 題はドキュメントのコメント欄にではなく、 django-users メーリングリストIRC の #django チャネル にお願いします。

プレーンテキスト版

オフラインで読みたい人や手早く読みたい人のために、Django ドキュメントはプレー ンテキスト形式で読めます。

Django の公式リリース版を使っているなら、ソースコードのアーカイブパッケージ (tarball) に docs/ ディレクトリが入っています。このディレクトリには各リ リースの全てのドキュメントが入っています。

Django の開発版 (いわゆる Subversion “trunk”) を使っている場合、 docs/ ディレクトリに全てのドキュメントが入っています。最新版を取得したければ、 Python コードの更新と同様、 svn update を実行してください。

最新の Django ドキュメントを Subversion から取り出すには、以下のようなシェ ルコマンドを使います:

$ svn co https://code.djangoproject.com/svn/django/trunk/docs/ django_docs

テキストドキュメントの便利でローテクな使い方の一つに Unix の grep ユー ティリティを使った全ドキュメント検索があります。例えば、以下のようにすれば、 “max_length” について触ている部分を表示できます:

$ grep -r max_length /path/to/django/docs/
ローカルで HTML で読む

以下のステップを踏めば、 HTML ドキュメントのローカルコピーを手に入れられま す:

  • Django のドキュメントは、 Sphinx というシステムを使ってプレーンテキ ストから HTML への変換を行っています。 Sphinx のウェブサイトから Sphinx をダウンロードしてインストールするか、 pip を使って インストールします:

    $ sudo pip install Sphinx
    
  • Django のドキュメントディレクトリにある Makefile を使って、ドキュ メントを HTML に変換します:

    $ cd path/to/django/docs
    $ make html
    

    GNU Make がインストールされている必要があります。

    もし Windows の場合は、ドキュメントディレクトリにあるバッチファイルを 使用して変換できます:

    cd path\to\django\docs
    make.bat html
    
  • HTML ドキュメントが docs/_build/html に生成されます。

Note

Django ドキュメントは Sphinx バージョン 0.6 以上を使って生成できます が、 Sphinx 1.0.2 以上を使用することをおすすめします。

バージョン間の相違点

前述したように、 Subversion リポジトリに入っているテキストドキュメントは 変更や追加によって「最新最良」の状態にあります。変更によって、開発版、すな わち Subverion (“trunk”) 版の Django に新たに登場した機能がテキストに記載さ れることがよくあります。このため、 Django の各バージョン間で一貫したドキュ メンテーションポリシをここで示しておきます。

我々は、以下のポリシに従っています:

  • djangoproject.com の第一のドキュメントは Subversion から生成される HTML 形式のドキュメントです。これらのドキュメントは常に最新の Django 公式リリースと、最新のリリース 以後 に追加/変更された機能に対応し ています。
  • Django の開発版に機能を追加する場合、可能ならば同じ Subversion のコミッ トトランザクションにおいてドキュメントの変更もチェックインします。
  • 追加/変更された機能を区別するため、「バージョン X.Y で新たに追加され た機能です (New in version X.Y) という文を使います。X.Y は次の (開発 中の) リリースバージョンです。
  • 特定のリリース版のドキュメントは、公式リリース時に一度フリーズされま す。従って、ドキュメントはその時のスナップショットです。以前のバージョ ンに遡ってセキュリティアップデートその他の変更を行った場合にのみ、例 外的にドキュメントを更新します。ドキュメントのフリーズ後は、各ドキュ メントの冒頭に “These docs are frozen for Django version XXX” という 一文と、ドキュメントの最新版へのリンクを追加します。

See also

Python の初心者なら、まずは Python で何ができるかを理解しましょう。 Django は 100% Python で書かれているので、ほんの最小限 Python を理解す るだけで Django をより深く理解できるはずです。

プログラミング全般の初心者なら、 非プログラマのための Python リソース一覧 から始めてみるとよいでしょ う。

Python 以外の言語を学んだことがあって、 Python を素早く学びたいのなら、 Dive Into Python をお勧めします (木を伐って作ったヤツ もあります)。 Dive Into Python があまりお好きでなくても、 Python の本 はたくさんあ りますよ。

訳注: 日本語の読者のために

日本語の Python の情報を探しているなら、 日本 Python ユーザ会のページ を訪れてみてください。

日本語で書かれた Python の書籍を探しているなら、 http://wiki.python.org/moin/JapanesePythonBooks もチェックしてみて ください。

Django を使う

revision-up-to:17812 (1.4)

このセクションでは、Django を使う上での鍵となる要素について解説しています。

Django のインストール

revision-up-to:17821 (1.4) unfinished

このドキュメントを読めば Django を動かせるようになります。

Python のインストール

Django は Python の Web フレームワークなので、当然 Python が必要です。

Djangoを動かすには2.5から2.7までのバージョンのPythonが必要です。 (後方互換性の確保のため、Python3.0ではDjangoは動作しません。 the Django FAQ を参考にすれば、Python3.0のサポートに関してもっと詳しい 情報を手に入れることが出来ます。

http://www.python.org から Python を取ってきましょう。 Linux や Mac OSX を 動かしているのなら、おそらくインストール済みのはずです。

Jython で Django を動かす

Jython (Java プラットフォームで動く Python 実装) を使っているなら、他 にもいくつかステップを踏む必要があります。詳しくは Jython 上で Django を動かす を参照してください。

Windows上のPython

Windows上では、 PATH の環境を設定しなければなりません。パスをPython の実行可能ファイルとそのスクリプトまで通す必要があります。例えば、もし、 C:\Python27\ にPythonがインストールがされているならば、追加しなけ ればいけない PATH は:

C:\Python27\;C:\Python27\Scripts;

Apache と mod_wsgi のインストール

単に Django を試してみたいだけなら、この節は読み飛ばして次の節を読んでくだ さい。Djangoにはテスト用の軽量なWebサーバが付属しているので、運用環境での動 作が必要になるまでApacheをセットアップする必要はないのです。

Django を実運用するサイトで使いたい場合、 Apache と mod_wsgi を使って下 さい。mod_wsgiにある二つのモードのうち一つを選んで実行できます。エンベデッド モードとデーモンモードがそれです。エンベデッドモードでは、 mod_wsgi は mod_perl のようなもので、 Python を Apache の中に埋め 込み、サーバの起動時に Python コードをメモリにロードします。コードは Apache プロセスが生きている間ずっとメモリ上に存在するので、他のサーバ構成よりも明 らかに高いパフォーマンスを実現します。デーモンモードでは、mod_wsgiは 独立した要求処理のいらないデーモンプロセスを呼びます。 デーモンプロセスはWebサーバーをこえた異なるユーザーとして動作します、これは セキュリティの向上へと導く可能性もあり、そしてデーモンプロセスは Apache Webサーバの 完全なリスタートすることなしでデーモンプロセスを再開でき、あるいはコードベース をよりシームレスなものへとレフレッシュさせるでしょう。 どちらのモードが適切かどうかは、mod_wsgi のドキュメントを確認して決断してください。 まずは mod_wsgi が動作する Apache がインストールされていることを確かめてください。 Django は mod_wsgi をサポートする全てのバージョンの Apache で動作します。

mod_wsgi をインストールした後の設定方法は mod_wsgiでDjangoを動かす を参照してく ださい。

何らかの理由で mod_wsgi を使えない場合でも心配はいりません Django を運用す る方法は他にもあります。お勧めの第二選択肢は、 mod_wsgi の前身、 uWSGI です。これは nginx.と一緒に動きます。もう一つは FastCGI これはApache以外のDjangoと一緒にサーバーを動かす完璧な方法です。付け加えれば、 Django は WSGI 仕様(PEP 3333)に従っているので、他にも様々なサーバプラットフォームで 動作させられます。個々のプラットフォームにおけるインストール方法の説明は サーバ構成に関する wiki ページ を参照してください。

データベースを動かす

Django のデータベース API 機能を使うのなら、データベースサーバを動かす必要 があります。 Django は PostgreSQL, MySQL, Oracle および SQLite で動作し ます (SQLite はサーバを動かさなくても使えます)。

公式にサポートされたデータベースに加えて、Djangoで他のデータベースを使うために サードパーティからバックエンドが提供されています。

DjangoとORM機能は非公式なバックエンドによってよく考慮されてサポートされてい ます。クエリは非公式なバックエンドの明確な機能として扱っています、サポートささ されたクエリにそって、おのおののサードパーティのプロジェクトによって提供される サポートチャンネルによって管理されているでしょう。

さらに、各データベースの Python バインディングをインストールしておく必要が あります。

  • PostgreSQL を使う場合、 postgresql_psycopg2 パッケージが必要です。 このデータベースについての特有の詳細が欲しいのならば、 PostgreSQL notes を参照するとよいでしょう。

    Windows 環境なら、非公式の Windows むけコンパイル済みパッケージ を使っ て下さい。

  • MySQL を使う場合は、バージョン 1.2.1p2 以降の MySQLdb が必要です。 MySQL バックエンド にまつわるデータベース固有の説 明も読んでおいたほうがよいでしょう。

  • もし、サポートされていないサードパーティのバックエンドを用いる場合、 更なる要望に応えるために提供されているドキュメントを読んでください。

Django の manage.py syncdb を使って、モデルに対応するテーブルを自動生成 するつもりなら、Django に対して、データベース上でテーブルの生成や ALTER を 行える権限を付与せねばなりません。テーブルを手動で生成するのなら、Django に 付与する権限は SELECT, INSERT, UPDATE および DELETE だけで かまいません。ただし、データベースによっては、 syncdb 時に ALTER TABLE 権限を付与しておく必要があります。 syncdb で一度テーブ ルを生成してしまえば、 syncdbALTER TABLE 文を発行しません。

テストフレームワーク を使ってデータベースクエリのテ ストを行うのなら、データベースを生成する権限も必要です。

旧バージョンの Django の除去

以前のバージョンからアップグレードする形で Django をインストールする場合、 新しいバージョンをインストールする前に、まず旧バージョンをアンインストール しておく必要があります。

setup.py install を使って Django をインストールした場合は簡単で、 Python の site-packages ディレクトリから django ディレクトリを削除 するだけです。

Python egg を使って Django をインストールした場合、Django の .egg ファ イルを削除するとともに、 easy-install.pth から Django の .egg ファ イルパスが書かれたエントリを削除します。 easy-install.pth.egg は、通常 site-packages ディレクトリ下にあります。

site-packages はどこにあるの?

site-packages の在処はオペレーティングシステムや Python のインストー ル場所によって異なります。 site-pacakges の場所を調べるには、以下の コマンドを実行してみてください:

python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"

(このコマンドはPythonの対話プロンプトではなく、シェルプロンプトで実行してください。)

LinuxディストリビューションのDebianは分割する site-packages というデ ィレクトリをユーザがインストールしたパッケージのために持っていて、 Djangoをダウンロードしたtarballファイルからインストールするような時に使う。 上にあげたコマンドはシステムの site-packages を渡します、ユーザのディレクトリは /usr/local/lib/ にあり、 /usr/lib/ ではありません。

Django コードのインストール

インストール方法の説明は、 OS ごとのインストールパッケージを入れる場合、公 式リリース版を入れる場合、そして最新の開発バージョンを入れる場合で少し異な ります。

どの方法を選ぶにしても、そんなに難しくはないので安心してください。

OS ごとのインストールパッケージによるインストール

ディストリビューション固有の注意 を調べて、自分 のプラットフォーム/ディストリビューションで公式の Django パッケージやインス トーラが提供されていないか調べます。ディストリビューション固有の配布パッケー ジは、たいてい依存関係のあるパッケージを自動的にインストールしてくれたり、パ スの更新を行ってくれたりします。

公式リリースのインストールを pip

これはDjangoをインストールする上でおすすめの方法です。

  1. pip をインストールします。最も簡単な方法は、 standalone pip installer を使うことです。もし、 pip がインストー ルされているディストリビューションなら、古いものであればアップデートす る必要ががあるかもしれません。(もし古いものであれば、インストールがう まく動きません)

  2. (オプション) virtualenvvirtualenvwrapper をみてみましょう。 これらのツールは、個別の Python 環境を構築することができます、 システム全体にパッケージをインストールするよりも実用的です。 これらは、管理者権限なしでもパッケージをインストールできます。 これらのツールを学んだり使うかを決めるのはあなた次第です。

  3. もし Linux や MacOsX か、ほかの Unix 系統のOSを使用している場合、 sudo pip install Django をシェルコマンドで入力します。 Windows を使っているならば、コマンドシェルを管理者権限で起動し、 pip install Django コマンドを走らせます。 これらで、Python インストールのなかの site-packages にDjangoが インストールされます。

    もし、あなたが virtualenv を使っているなら、 sudo やadministrator (管理者)の権限を使うことなく、virtualenv の site-packages ディレクトリに Django がインストールされます。

公式リリースのインストール
  1. ダウンロードページ から、最新版の Django をダウンロードします。
  2. ダウンロードしたファイルを tar で展開します。 (例: tar xzvf Django-NNN.tar.gz)。 Windows を使っているのなら、 コマンドラインツール bsdtar を使うか、 7-zip のような GUI ベースの ツールを使います。
  3. ステップ 2 で生成された展開先のディレクトリに移ります (例: cd Django-NNN)
  4. Linux, Mac OS X, その他の Unix 系のオペレーティングシステムでは、 シェルプロンプトからコマンド sudo python setup.py install を入力 します。 Windows を使っているのなら、管理者権限でコマンドプロンプト を起動して、コマンド setup.py install を実行します。 上のコマンドを実行すると、 Django は Python インストールディレクトリの site-packages ディレクトリ下にインストールされます。
開発バージョンのインストール

Django の開発版を追いかける

最新版の Django を使うと決めたのなら、 開発の進行状況 によく注意して、 以前のバージョンと互換性のない変更 がないか気をつけておく必要がある でしょう。これらのリンク先のページに注意していれば、使ってみたい新たな 機能や、手元の Django をアップデートする際に、自分のコードのどこを修正 する必要があるか追いかけられます。 (安定版では、 Django のアップデート 時に必要な変更は全てドキュメントに記載されています。)

Django のコードを更新して最新のバグフィクスや改良を適用したいのなら、以下の 説明に従って開発バージョンをインストールしてください。

  1. Subversion, Git, Mercurial がインストールされていて、シェルからコマンドを実行できること を確認してください。 (調べるには、シェルプロンプトで svn helpgit help, hg help を入力します。) Subversionリポジトリは、公式のGitとMercurialリポジトリ自体が最新のものとな なるような標準のソースということを覚えておいてください。

  2. 以下のようにして、Django のメイン開発ブランチ (‘trunk’) をチェックアウト します:

    # Subversion
    svn co https://code.djangoproject.com/svn/django/trunk/ django-trunk
    

    Subversionリポジトリのミラーはこんな風に取得できます

    # Git (requires version 1.6.6 or later)
    git clone https://github.com/django/django.git
    # or (works with all versions)
    git clone git://github.com/django/django.git
    
    # Mercurial
    hg clone https://bitbucket.org/django/django
    

    Warning

    これらのミラーは五分ごとに最新版にアップデートされます。 けれど、サービス上にホストされてから常に五分ごとにアップデートされている という保証がついているわけではないので注意してください。

  3. Python インタプリタが Django のコードをロードできるようにします。一番便利な 方法は、 Python パスの修正 することです。 .pth ファイルに django-trunk ディレクトリへのフルpathをシステムの site-packages ディレクトリまで追加することです。例えば Unixライクなシステムでは

    echo WORKING=DIR/django-trunk > SITE-PACKAGES-DIR/django.pth
    

    (上の作業では、使っているプラットフォームの site-packages ディレク トリに合わせて SITE-PACKAGES-DIR を書き換えてください。 site-packages の場所の探し方は、 site-packages はどこにあるの? を参照してください)

  4. django_src/django/bin/django-admin.py を実行できるようにします。 Unix系のシステムでは、例えば以下のようにして、 /usr/local/bin のよう なシステムパス上にシンボリックリンクを作成します:

    ln -s WORKING-DIR/django-trunk/django/bin/django-admin.py /usr/local/bin
    

    (上の行では、WORKING-DIRを新しい django-trunk ディレクトリまでのフル pathとマッチするように変えてください)

    これで、 django-admin.py とコマンド入力するだけで、どのディレクトリ 上でも django-admin.py を起動できるようになります。

    Windows では、 django_src/django/bin/django-admin.py を、 C:\Python27\Scripts のようなシステムパス上にコピーします。

Warning

sudo python setup.py install を走らせないでください。なぜなら、 ステップ3から4までの行程はそれと同等のことを行っているからです。 もっといえば、これはDjangoを最新のバージョンにアップデートする際に 問題を引き起こすものとして知られています。

Django のソースコードを更新する際には、 django-trunk ディレクトリで svn update を実行してください。実行すると、 Subversion が更新部分を自動 的にダウンロードします。同等のコマンドをGitで行うには git pull, Mercurialでは hg pull --update を実行します

モデルとデータベース

revision-up-to:17812 (1.4)

モデルとは、サイトを構成するデータの、ただ一つかつ最終的なデータソースを指 します。モデルには、保存したいデータに不可欠なデータフィールドと、その振舞 いが収められています。一般的に、各モデルは単一のデータベーステーブルに対応 づけられています。

モデルの作成

revision-up-to:17812 (1.4)

モデルとは、サイトを構成するデータの、唯一絶対的なデータソースを指します。 モデルには、保存したいデータを表すデータフィールドと、データのビヘイビアを 定義します。通常、一つのモデルは一つのデータベーステーブルに対応しています。

モデルの基本として、以下のことを知っておいてください:

  • 各モデルは Python のクラスで、 django.db.models.Model のサブ クラスです。
  • モデルの各アトリビュートの値は、データベース上のあるフィールドを表現 します。
  • モデルの情報をもとに、 Django はデータベース API を自動生成します。 API の詳細は「 クエリを生成する 」で解説します。
簡単な例

以下の例では、 first_name および last_name というフィールドを持った Person モデルを定義しています:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_namelast_name はモデルの フィールド (fields) です。各 フィールドはクラスのアトリビュートとして定義します。各々のアトリビュートは データベースのカラムに対応します。

上の Person モデルは、以下のようなデータベーステーブルを生成します:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

いくつか、技術的な注意点があります:

  • テーブルの名前 myapp_person はモデルのメタデータから自動的に決定 します。テーブル名は変更できます。「 テーブル名 」を参照して ください。
  • id は自動的に追加されます。この動作も変更できます。 「 主キーフィールドの自動設定 」を参照してください。
  • 上の例の CREATE TABLE SQL 文は PostgreSQL の書式で書かれています が、 Django は 設定ファイル に指定しておいた データベースバックエンドに合わせて適切な SQL を発行します。
モデルを使う

モデルを定義したら、そのモデルを 使う よう Django に指示しましょう。設定 ファイルを編集して、 models.py の入っているパッケージの名前を INSTALLED_APPS 設定に追加してください。

例えば、アプリケーションのモデルが mysite.myapp.models モジュール (mysite.myappmanage.py startapp スクリプトで 作成したアプリケーションのパッケージ) に入っているなら、 INSTALLED_APPS に以下のように書きます:

INSTALLED_APPS = (
    #...
    'mysite.myapp',
    #...
)

新しいアプリケーションを INSTALLED_APPS に追加したら、 manage.py syncdb を忘れずに実行してください。

フィールド

モデルのフィールドは、モデルの定義で最も重要な、かつ最小限必要な要素です。 フィールドは、以下の例のように、クラスのアトリビュートとして定義します:

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()
フィールドのタイプ

モデルの各フィールドには、 Field クラスのサブク ラスから、適切なクラスのインスタンスを生成して入れます。Django は、フィール ドのクラスが何であるかという情報を、以下のような判定で使います:

  • データベースのカラム型 (INTEGER, VARCHAR など) の決定
  • Django の管理サイトで使うウィジェット (<input type="text">, <select> など) の決定
  • Django の管理サイトやマニピュレータが実施する最小限のバリデーション の決定

Django にはたくさんのフィールドクラスが組み込みで付属しています。組み込みの フィールドクラス一覧は「 モデルフィールドリファレンス 」にあります。組み込みのフィールドで実現できないことが あれば、フィールドクラスを自分で定義できます。 「 howto-custom-model-fields 」を参照してください。

フィールドのオプション

各フィールドクラスには、それぞれフィールド固有の引数がいくつかあります (詳 しくは「 モデルフィールドリファレンス 」を参照し てください)。例えば、 CharField (とそのサブクラ ス) には、データを保存するときの VARCHAR データベースフィールドの長さを 決める必須の引数、 max_length が必要で す。

また、全てのフィールドに共通の引数もあります。共通の引数はどれも省略可能で す。共通の引数は「 リファレンス 」で説 明しています。

ここでは、良く使う引数を簡単に紹介しましょう:

null
True にすると、フィールドの値が空のとき、データベースには NULL を保存します。デフォルトの設定は False です。
blank

True にすると、フィールドの値を空白 (blank) にできます。デフォ ルトの設定は False です。

null とは違うので注意してください。 null は、単に空のデータをデータベース上でどう表現す るかを決めています。一方、 blank はフィールドの値の 検証 (validation) 方法を決めています。あるフィールドに blank=True を指定すると、 Django の管理サイト 上で、そのフィールドを空の値にしたままエントリを作れます。 blank=False にすると、フィールドには必ず値を 入れねばなりません。

choices

iterable (リストまたはタプル) を指定します。 iterable の各要素は フィールドの値の選択肢を表す 2 要素のタプルです。 choices を指定すると、 Django の管理サイト上には、標 準的なテキストフィールドの代わりにセレクタボックスが表示され、ユー ザは選択肢の値だけを選べます。

選択肢のタプル列は、例えば以下のように定義します:

YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
)

各タプルの最初の要素が実際にデータベースに保存される値で、二つめの 要素が管理インタフェースや ModelChoiceField に表示される内容で す。あるモデルオブジェクトのインスタンスで後者の値 (表示値) を取り 出したければ、 get_FOO_display メソッドを使います。例えば、以下 のようなモデルを考えましょう:

from django.db import models

class Person(models.Model):
    GENDER_CHOICES = (
        (u'M', u'Male'),
        (u'F', u'Female'),
    )
    name = models.CharField(max_length=60)
    gender = models.CharField(max_length=2, choices=GENDER_CHOICES)

gender の表示値は get_gender_display で取り出します:

>>> p = Person(name="Fred Flinstone", gender="M")
>>> p.save()
>>> p.gender
u'M'
>>> p.get_gender_display()
u'Male'
default
フィールドのデフォルト値です。この値は、固定の値でも、関数などの呼 び出し可能オブジェクトでもかまいません。呼び出し可能オブジェクトに した場合、新たなモデルインスタンスを生成するたびにそのオブジェクト を呼び出して、フィールドのデフォルト値を動的に決定します。
help_text
管理サイトでフォームの下に表示される、詳しい「ヘルプ」テキストです。 管理サイトを使っていなくても、定義しておけばドキュメントとして役に 立ちます。
primary_key

True を指定すると、フィールドをモデルの主キーにします。

モデルのどのフィールドにも primary_key=True が指定されていない場合、Django は自動的に IntegerField を追加して主キーを保存します。従って、主キー の設定をデフォルトの状態から変更したいのでないかぎり、 primary_key=True を指定する必要はありま せん。詳しくは「 主キーフィールドの自動設定 」を参照してく ださい。

unique
True の場合、フィールドの値はテーブル全体で一意でなければなりま せん。

前置きしたように、ここではよく使うオプションを簡単に説明するにとどめます。 全オプションの詳しい情報は 「 共通のモデルフィールドオプションのリファ レンス 」を参照してください。

主キーフィールドの自動設定

デフォルトの動作では、 Django は各モデルに以下のフィールド:

id = models.AutoField(primary_key=True)

を追加します。このフィールドは主キーに使われ、フィールドの値はレコードの追 加ごとに自動的にインクリメントされてゆきます。

特定のフィールドを主キーにしたければ、フィールドのオプションに primary_key=True を指定してください。 Field.primary_key の設定されたフィールドの定義があれば、Django は id カラムを自動的に追加しません。

各モデルには必ず一つ primary_key=True のフィー ルドが必要です。

表示用のフィールド名

ForeignKeyManyToManyFieldOneToOneField を除くフィールドクラスは、 第一引数に表示用のフィールド名を指定できます。この引数は省略可能です。引数 を指定しない場合、 Django がフィールドのアトリビュート名のアンダースコアを スペースに置き換えて、表示用のフィールド名を自動的に生成します。

下の例では、表示用のフィールド名は "person's first name" です:

first_name = models.CharField("person's first name", max_length=30)

下の例では "first name" です:

first_name = models.CharField(max_length=30)

ForeignKey, ManyToManyField, および OneToOneField クラスでは、第一引数は必須 で、リレーション先のモデルのクラス名を指定せねばなりません。これらのフィー ルドクラスでは、 verbose_name キーワード引数を使って表示用の フィールド名を指定します:

poll = models.ForeignKey(Poll, verbose_name="the related poll")
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(Place, verbose_name="related place")

慣習的に、 verbose_name の先頭の文字は大文字にしません。 Django は必要なときに先頭の文字を自動的に大文字にします。

リレーション

リレーショナルデータベースの威力が、テーブルを相互に関連づけする機能である のはいうまでもありません。Django には、多対一 (many-to-one)、多対多 (many-to-many)、一対一 (one-to-one) といった、よく使うリレーションを定義す る機能があります。

多対一のリレーション

多対一のリレーションを定義するには ForeignKey を使います。このフィールドは他 のフィールド型と同じように、モデルのクラスアトリビュートとして定義できます。

ForeignKey は、リレーションを張る対象のク ラスを指定する必須の引数を一つとります。

例えば、 Car モデルに Manufacturer というフィールドを持たせたいとし ましょう。すなわち、ある Manufacturer には複数の Car が対応するが、 各 Car には一つだけ Manufacturer が対応するようにしたいとしましょう。 この場合、以下のように定義します:

class Manufacturer(models.Model):
    # ...

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer)
    # ...

再帰的リレーション (自分自身に対する多対多 のリレーション) や、 未定義のモデルに対するリレーション も定義できます。「 モデルフィールドのリファレン ス 」を参照してください。

必須ではありませんが、 ForeignKey の名前 (上の例では manufacturer) には、モデル名を小文字にしたものを使うよう勧 めます。もちろん、以下のように、好きな名前を付けてもかまいません:

class Car(models.Model):
    company_that_makes_it = models.ForeignKey(Manufacturer)
    # ...

See also

ForeignKey フィールドは モデルフィールドリファレンス で説明してい る多くの追加引数を受け取ります。これらのオプションはリレーションがどのよ うに動くべきかを定義するのに役立ちます。

逆参照されるオブジェクトへのアクセスについては リレーションの逆参照の例. に詳しく書いてあります。

多対多のリレーション

多対多の (many-to-many) リレーションを定義するには ManyToManyField を使います。このフィール ドは他のフィールド型と同じように、モデルのクラス属性に含めて使えます。

ManyToManyField には固定引数が一つあり、 リレーションを張る対象のクラスを指定します。

Pizza には複数の Topping オブジェクトを関連付ける例を考えてみましょ う。すなわち、ある Topping は複数のピザの上に置けて、逆にそれぞれのピザ には複数のトッピングを置けるというわけです。このリレーションを表現するには 以下のように定義します:

class Topping(models.Model):
    # ...

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

ForeignKey と同様、 再帰的リレーション (自分自身に対する多対多のリレーション) や、 未定義のモデルに対するリレーション も定義できま す。「 モデルフィールドのリファレンス 」を参照して ください。

必須ではありませんが、 ManyToManyField の 名前 (上の例では toppings)は、リレーション先のモデル名の複数形にするよ う勧めます。

どちらのモデルで ManyToManyField を定義し てもかまいませんが、どちらか片方のモデルだけ指定すべきです – 両方には指定 すべきではありません。

一般的に言って、 Django の管理インタフェースを使うのなら、 ManyToManyField インスタンスを入れておく のは、管理インタフェースで編集される側のオブジェクトにしておくとよいでしょ う。上の例では、 topping は (ToppingManyToManyFieldpizzas をもたせる のではなく) Pizza に入れておきます。というのも、「トッピングを乗せるピ ザ」を編集するよりも「ピザの上に乗せるトッピング」を編集する方が自然だから です。というわけで、上の例のようにすれば、管理サイトの Pizza の編集画面 上でユーザにトッピングを選ばせられます。

See also

ManyToManyField フィールドも モデルフィールドリファレンス で説明して いる多くの引数を受け取ります。これらのオプションはリレーションがどの ように動くべきかを定義するのに役立ちます。必須ではありません。

エクストラフィールドで多対多を定義する

ピザとトッピングを組み合わせたり照合したりするだけのような多対多のリレーショ ンを扱いたいのなら、標準の ManyToManyField で事 足ります。しかし、時として、二つのモデルのリレーションそのものにデータを関 連づけたい場合があります。

例えば、ミュージシャンのグループと、在籍しているミュージシャンを追跡するア プリケーションのケースを考えてみましょう。これは個人 (person) とグループの 関係なので、 ManyToManyField でリレーションを表 現できるはずです。しかし、ミュージシャンとグループの関係には、あるミュージ シャンがあるグループにいつ合流したか、のような細かい情報がたくさんあります。

こうした状況に対応するために、Django には、あらかじめリレーションを管理する ためのモデル (中間モデル) を定義しておき、その中間モデルに向けてエクストラ フィールド (extra field) を組み込んで多対多のリレーションを定義する方法があ ります。中間モデルは ManyToManyFieldthrough 引数に指定します。ミュージシャン の例は以下のように書けます:

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __unicode__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __unicode__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

中間モデルからは、多対多のリレーションの両側にあたるモデルに対して明示的に 外部キーを張らねばなりません。

中間モデルには、いくつか制約があります:

  • 中間モデルは、多対多リレーションのターゲットモデル (リレーションを張 られる側のモデル) に対する外部キーを 一つだけ しか定義できません (上の例では Person ですね)。同じモデルに複数の外部キーを定義する と、モデルのバリデーション時にエラーを送出します。
  • 中間モデルは、多対多リレーションのソースモデル (リレーションを張る側 のモデル) に対する外部キーを 一つだけ しか定義できません (上の例で は Group ですね)。同じモデルに複数の外部キーを定義すると、モデル のバリデーション時にエラーを送出します。
  • 自分自身に対する多対多のリレーションを定義するのに中間モデルを使う場 合にのみ、例外的に同じモデルに対するリレーションを二つ定義できます。 ただし、多対多リレーションの中では、二つの外部キーは別々、すなわちソー スとターゲットを区別して扱います。
  • 中間モデルを使って、あるモデルから自分自身に多対対のリレーションを定 義する場合、 symmetrical=False を指定せねばなりません (「 モデルフィールドのリファレンス 」を参照してください) 。

さて、これで中間モデル (例でいうところの Membership) を使った ManyToManyField を設定したので、多対多のリレーショ ンを生成できます。リレーションの生成は、中間モデルのインスタンス生成によっ て実現します:

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason= "Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<Group: The Beatles>]
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason= "Wanted to form a band.")
>>> beatles.members.all()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

通常の ManyToManyField と違って、 addcreate, (beatles.members = [...] のような) 代入では、リレーションの定義は行えま せん:

# 使えません。
>>> beatles.members.add(john)
# これも使えません。
>>> beatles.members.create(name="George Harrison")
# これもまた使えません。
>>> beatles.members = [john, paul, ringo, george]

なぜでしょうか。中間モデルでリレーションを作成するときには、単に PersonGroup の間のリレーションだけではなく、 Membership テーブルのレコー ドを生成するための情報が全て必要だからです。単に addcreate を呼 び出したり代入を行うだけでは、リレーション以外の情報を埋められません。その 結果、中間モデルを使う場合は、 addcreate は使えないのです。 中間モデルを使っている場合、多対多のリレーションを保存するには、中間モデル のインスタンスを生成するしかない、ということを覚えておいてください。

同じ理由で remove() メソッドも使えません。 ただし、 clear() メソッド を使えば、以下のように多対多のリレーションを一括して消去できます:

# ビートルズ解散
>>> beatles.members.clear()

ひとたび中間モデルによる多対多のリレーションを作成したら、通常の多対多リレー ションと同じように、リレーション先のモデルのアトリビュートを使ってリレーショ ンをまたいだクエリを実行できます:

# 'Paul' で始まる名前の人がメンバにいるグループを探す
>>> Groups.objects.filter(members__name__startswith='Paul')
[<Group: The Beatles>]

中間テーブルを使っているので、中間テーブルのアトリビュートを使ったクエリも 実行できます:

# 1961年1月1日以降に合流した Beatles のメンバを探す
>>> Person.objects.filter(
...     group__name='The Beatles',
...     membership__date_joined__gt=date(1961,1,1))
[<Person: Ringo Starr]

メンバの情報にアクセスする必要があれば、 Membership モデルのクエリを 直接実行することができます:

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
u'Needed a new drummer.'

別の方法として、Person オブジェクトから 多対多の逆参照 クエリを使うことによって も同じ情報にアクセスできます。

>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
u'Needed a new drummer.'
一対一のリレーション

一対一のリレーションを定義するには、 OneToOneField を使います。このフィールド も他のフィールド型と同じように、モデルクラスのアトリビュートとして定義しま す。

このリレーションがもっとも有用なのは、あるオブジェクトが別のオブジェクトを 何らかの形で「拡張」している場合の主キーとして使う場合です。

OneToOneField には固定引数が一つあり、リ レーションを張る対象のクラスを指定します。

例えば、「場所 (place)」のデータベースを作るときには、アドレス、電話番号、 といった標準的なものを作成します。その上で、レストランデータベースを作成 するときに、 Restaurant モデルの中に同じフィールドをつくるような 繰り返し作業 (repeat yourself) をする代わりに、場所データベースを基盤にして レストランのデータベースを作成したいとしましょう。このとき、 Restaurant には Place への OneToOneField をもた せられます (レストランは場所に対して “is-a” の関係だからです。実際には、こ のような状況を扱うのに モデルの継承 を使いますが、 モデル継承は OneToOneField で実現されてい ます)。

ForeignKey と同様、 再帰的リレーション未定義のモデルに対するリレーション も定義できま す。詳しくは「 モデルフィールドのリファレンス 」を参 照してください。

See also

OneToOneField フィールドは、オプションの引数を一 つとります。引数は「 モデルフィールドリファレンス 」 で解説しています。

以前は、モデル中で OneToOneField を使うと、 そのフィールドは自動的にモデルの主キーになっていました。この仕様はもうなく なりましたが、主キーにしたければ手動で primary_key に指定できます。この 変更により、一つのモデル中で複数の OneToOneField を使えるようになりました。

ファイル間にまたがるモデル

あるモジュールから別のモジュールへのリレーションは全く問題なく張れます。リ レーションを張るには、以下の例のようにモデル定義の先頭でリレーション対称の モデルを import しておき、必要なところでそのモデルクラスを参照します:

from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(ZipCode)
フィールド名の制約

Django がモデルのフィールド名に課している制約は二つしかありません:

  1. フィールド名は Python の予約語であってはなりません。さもないと Python のシンタクスエラーを引き起こすからです。

    悪い例:

    class Example(models.Model):
        pass = models.IntegerField() # 'pass' は予約語です!
    
  2. フィールド名には二つ以上の連続するアンダースコアを入れてはなりません。 なぜなら、Django は二重アンダースコアをクエリ照合構文で使っているか らです。

    悪い例:

    class Example(models.Model):
        foo__bar = models.IntegerField() # 二重アンダースコアがあります!
    

データベースのカラム名はフィールドの名前と一致していなくてもよいので、デー タベース側では制約を回避できます。詳しくは後述の db_column を 参照してください。

joinwhere, select のような SQL の予約語を、モデルのフィール ド名に使っても かまいません 。というのも、Django は SQL クエリを生成する ときにデータベーステーブル名やカラム名を常にエスケープするからです。エスケー プにはデータベースエンジン固有のクオート方式を使います。

カスタムのフィールド型

既存のモデルフィールドが目的とするアプリケーションに合わない場合や、あまり 一般的でないデータベースカラムタイプを活用したい場合のために、独自のフィー ルドクラスを作成できます。カスタムフィールドの詳しい作成方法は 「 howto-custom-model-fields 」で説明しています。

Meta オプション

モデルにメタデータを指定するには、以下のようにモデルの内部クラス Meta を使います:

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

モデルのメタデータとは、モデルインスタンスの整列順 (ordering)や、データベース上のテーブル名 (db_table)、モデル名の単数形や複数形 (verbose_name, verbose_name_plural) といっ た、「フィールドの情報でない」情報です。必須のメタデータはなく、 class Meta すら省略できます。

Meta に指定できるオプションの一覧は、「 モデルオプションのリファ レンス 」で解説しています。

モデルのメソッド

カスタムの行レベル (“row-level”) の機能をオブジェクトに実装するには、カスタ ムのメソッドを定義します。 Manager メソッドの目 的が「テーブル級 (table-wide)」 の操作であるのに対し、モデルメソッドは個々 のモデルインスタンス単位の操作を実現します。

モデルメソッドは、ビジネスロジックの置き場所をモデル一箇所に限定するための 効果的なテクニックです。

例えば、以下のモデルでは、いくつかカスタムメソッドを定義しています:

from django.contrib.localflavor.us.models import USStateField

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()
    address = models.CharField(max_length=100)
    city = models.CharField(max_length=50)
    state = USStateField() # Yes, this is America-centric...

    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31):
            return "Baby boomer"
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        return "Post-boomer"

    def is_midwestern(self):
        "Returns True if this person is from the Midwest."
        return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')

    def _get_full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)

この例の最後のメソッドは プロパティ (property) です。

モデルインスタンスのリファレンス 」では、 各モデルに自動的に付与されるメソッド を全て 紹介しています。ほとんどのメソッドはオーバライド可能です。詳しくは後述の 既存のモデルメソッドをオーバライドする を参照してください。といっても、 たいていはせいぜい以下の 2 つのメソッドをオーバライドするぐらいで済むでしょ う:

__unicode__()

オブジェクトの「Unicode 表現」を返す、Python の「マジックメソッド」 です。 Python や Django はこのメソッドを使ってモデルインスタンスに 型強制を適用し、通常の文字列に変換したり表示したりしています。特に、 対話コンソールや管理インタフェース上でオブジェクトを表示するときに 使われています。

このメソッドは常に定義するよう勧めます。定義しておくと、いろいろと 便利だからです。

get_absolute_url()

このメソッドを定義しておくと、 Django にオブジェクトの URL を計算さ せられます。 Django の管理インタフェースなどで、オブジェクトに関連 した URL を特定するときにこのメソッドを使います。

オブジェクトを一意に特定できるような URL を持たせたいときには、この メソッドを定義しましょう。

既存のモデルメソッドをオーバライドする

他にも、データベースの動作をカプセル化していて、カスタマイズ可能な モ デルメソッド があります。特によく変更するのは、 save()delete() の動作でしょう。

こうしたモデルメソッドは、モデルオブジェクトの挙動を変えるために好きなだけ オーバライドできます。

組み込みメソッドをオーバライドする古典的なユースケースに、オブジェクトの保 存時に何か別の処理を行うというものがあります。例を示しましょう (save() のパラメタはドキュメントを参照してください。):

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # 「実際の」 save() を呼び出します。
        do_something_else()

また、以下のようにすれば保存を抑止できます:

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        if self.name == "Yoko Ono's blog":
            return # Yoko は自分のブログを持っていません!
        else:
            super(Blog, self).save(*args, **kwargs) # 「実際の」 save() を呼び出します。

スーパクラスのメソッド呼び出し、つまり super(Blog, self).save(*args, **kwargs) を忘れないでください。この呼び 出しを忘れると、デフォルトの処理が行われないため、データベースにデータが保 存されません。

*args, **kwargs によってモデルメソッドに渡せる引数をパススルーすること も重要です。 Django は時々ビルトインのモデルメソッドに新しい引数を追加して 拡張することがあります。メソッド定義で *args, **kwargs を使っておけば、 これらの引数が追加されても自動でサポートされることが保証されます。

削除のオーバーライド

QuerySet による一括オブジェクト削除 の時にはオブジェクトの delete() メソッドが呼ばれないこ とに注意してください。カスタマイズした削除ロジックが実行されることを 保証するには pre_deletepost_delete シグナルのいずれかまたは 両方を使えば良いでしょう。

カスタム SQL の実行

もう一つよくあるパターンは、カスタムの SQL 文をモデルメソッドやモジュールレ ベルのメソッドにに書くというものです。詳しくは、 「 素の SQL を扱う 」を参照してください。

モデルの継承

Django のモデルクラス継承は、 Python の通常のクラス継承とほぼ同じように動作 します。モデル継承を実現するには、一つだけきめておかねばならないことがあり ます。それは、親クラスを (独自のデータベーステーブルを持った) 独立のモデル にしたいか、親クラスを共通情報の単なるホルダとして使い、子クラスでその情報 を扱う形にしたいかです。

Django には 3 種類のモデル継承スタイルがあります。

  1. 複数の子モデルクラスでいちいち同じ情報を入力せずに済ませるために、親モ デルクラスに共通の情報を持たせたいことはよくあります。親モデルクラスを 単体で使うことがないのなら、 抽象ベースクラス を使うのがよ いでしょう。
  2. 一方、(他のアプリケーションなどにある) 既存のモデルをサブクラス化して拡 張したり、個々のモデルに固有のデータベースを持たせたいような場合には、 マルチテーブル継承 を使うのがよいでしょう。
  3. 親モデルクラスのフィールドには手を加えず、 Python レベルでの振る舞いだ けを変えたいのなら、 プロキシモデル を使うのがよいでしょう。
抽象ベースクラス

抽象ベースクラスは、たくさんのモデルに共通する情報を入れておきたいときに便 利な仕組みです。抽象ベースクラスを定義するには、ベースクラス定義の、 Metaabstract=True を入れておきます。モデルを 抽象ベースクラスとして定義すると、そのクラスはデータベーステーブルを生成し ません。その代り、他のモデルを定義するときに抽象ベースクラスを親にすると、 ベースクラスのフィールドが子クラスに追加されます。抽象ベースクラスで定義し たフィールドと同じ名前のフィールドを子クラスで定義しようとするとエラーを引 き起こします (Django は例外を送出します)。

例を示しましょう:

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

このように定義すると、 Student モデルは name, age および home_group の三つのフィールドを持つようになります。. CommonInfo モ デルは抽象ベースクラスなので、通常の Django モデルとしては使えません。 CommonInfo はデータベーステーブルを作らず、マネジャなども持ちません。

ほとんどの用途で使えるのは、このタイプのモデル継承でしょう。抽象ベースクラ スは、子クラスで共通の情報を Python レベルに切り出しながらも、データベース レベルでは、各子クラスに一つのデータベーステーブルを生成します。

Meta の継承

抽象ベースクラスを作成するとき、 Django は抽象ベースクラスの内部クラス Meta をクラス属性として参照できるようにします。子クラ スで Meta クラスを定義しない場合、親クラスの Meta がそのまま継承されます。子クラスで Meta を拡張したければ、親クラスをサブクラス化できます。 以下に例を示しましょう:

class CommonInfo(models.Model):
    ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'

Django は抽象ベースクラスの Meta クラスを生成するとき に一つだけ手を加えます。すなわち、 Meta の属性を組み 込む前に、 abstract=False を設定するのです。これにより、抽象ベースクラ スの子クラスが自動的に非抽象ベースクラスになります。もちろん、他の抽象ベー スクラスを継承した新たな抽象ベースクラスも定義できます。継承するには、明示 的に abstract=True をセットしてください。

Meta クラスの属性の中には、抽象ベースクラスで定義して も意味のないものもあります。例えば、 db_table を抽象ベースクラスの Meta で定義すると、その子クラス全て (で、独自に Meta を定義しないもの) が、同じデータベーステーブルを 使おうとしてしまいます。

マルチテーブル継承

Django では、継承の各階層にいるクラスが抽象クラスでない実際のモデルであるよ うな、もう一つのタイプのモデル継承をサポートしています。各モデルはそれぞれ が一個のデータベーステーブルを表現していて、個別にクエリを発行したり、イン スタンスを生成したりできます。継承の関係によって、親クラスと子クラスの間に は (自動生成された OneToOneField によって) リン クが張られます。以下の例で説明しましょう:

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField()

Place の全てのフィールドは、 Restaurant からも使えます。しかし、そ れぞれのフィールドのデータは別々のテーブルに格納されます。従って、以下のよ うな操作をどちらも実行できます:

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

例えば、 Place であり、かつ Restaurant でもあるようなオブジェクトが あれば、モデル名を小文字にした属性を使って、 Place から Restaurant を取り出せます:

>>> p = Place.objects.get(id=12)
# p が Restaurant オブジェクトなら、子クラスを返す:
>>> p.restaurant
<Restaurant: ...>

ただし、上の pRestaurant クラスで ない のなら (Place から 直接生成されたオブジェクトや、他のクラスの親クラスなら) p.restaurant はエラーを引き起こします。

Meta とマルチテーブル継承

マルチテーブル継承では、子クラスが親クラスの Meta ク ラスを継承する意味がありません。 Meta オプションは全 て親クラスだけに適用されるべきもので、他のクラスに同じ内容を適用しても、矛 盾した振る舞いを引き起こすだけです(これは、独自のテーブルを持たない抽象ベー スクラスと対照的な部分です)。

従って、マルチテーブル継承の子クラスは親クラスの Meta クラスにアクセスする方法を持ちません。ただし、限られたケースで、子クラスが 親クラスの挙動を継承する場合があります。それは、子クラスで ordering または get_latest_by 属性を指定していない場合に、 親クラスの属性を継承するというものです。

親クラスで整列カラムが指定されていて、子クラス側で整列カラムを指定したくな い場合は、明示的設定を無効にしてください:

class ChildModel(ParentModel):
    ...
    class Meta:
        # 親クラスの整列カラム設定の効果を除去する
        ordering = []
継承と逆リレーション

マルチテーブル継承は親クラスと子クラスを暗黙の OneToOneField でリンクするので、前述の例のように、 親クラスから子クラスをたどれます。しかし、子クラスをたどるときに使う名前は、 ForeignKeyManyToManyField リレーションを定義したと きのデフォルトの related_name と同じ値 を使っています。従って、子クラスで ForeignKeyManyToManyField を別のクラスに向けて張る ときには、 必ず related_name をフ ィールドに指定せねばなりません。指定し忘れると、 Django は validatesyncdb 時にエラーを送出します。

例えば、上の Place クラスに対して、 ManyToManyField を持った別のサブクラスを定義する には、以下のように書きます:

class Supplier(Place):
    # Must specify related_name on all relations.
    customers = models.ManyToManyField(Restaurant, related_name='provider')
親クラスのリンクフィールドを定義する

すでに触れたように、 Django は子クラスと抽象クラスでない親クラスにリンクし た OneToOneField を自動的に生成します。親クラス へのリンク名を操作したければ、自分で OneToOneField を作成し、パラメタに parent_link=True を渡 してください。

プロキシモデル

マルチテーブル継承 を使う時には、各々のモ デルのサブクラスに新しくデータベーステーブルが作られます。サブクラスはデー タフィールドを追加して、親クラスにはない新しいデータを保存する箇所を増やす ので、これは一般的には望まれる動作です。

しかしながら、デフォルトのマネジャーを変えたり新しいメソッドを追加すること によって、同じモデルに対して Python の動作を変えたいだけのときがあります。 これが、プロキシモデル継承がある理由です。オリジナルのモデルに対して プロキシ を作ります。そうすれば、ただオリジナルのモデルを使用しているよ うに、プロキシモデルのインスタンスを作成、更新、削除して、全てのデータをセ ーブすることが出来ます。違いは、モデルの順序づけやデフォルトのマネジャーを オリジナルを改変せずに変更できるということです。

プロキシモデルは通常のモデルと同じように宣言します。 Django に対してそのモ デルがプロキシモデルであることを教えるためには、 Meta クラス内の proxy アトリビュートを True にします。

例えば、標準の User モデルに対してテ ンプレートの中で用いるメソッドを追加したい場合、このように定義します:

from django.contrib.auth.models import User

class MyUser(User):
    class Meta:
        proxy = True

    def do_something(self):
        ...

MyUser クラスは親である User クラ スと同じデータベーステーブル上で動作します。特に、 User のどんな新しいインスタンスでも、 MyUser を通してアクセスすることができます。逆も可能です:

>>> u = User.objects.create(username="foobar")
>>> MyUser.objects.get(username="foobar")
<MyUser: foobar>

また、モデルに別のデフォルトオーダーを定義する場合にもプロキシモデルを用い ることができます。標準の User モデル ではオーダーが定義されていません (ソートはコストがかかるので、ユーザを取得 する時にいつもそうしたくないため、故意にそうしています。) プロキシを使って いて、いつも username 属性で順序づけたい場合は、こうすると簡単です:

class OrderedUser(User):
    class Meta:
        ordering = ["username"]
        proxy = True

こうすると、普通の User のクエリでは 順序が定められていませんが OrderedUser のクエリは username で順序 づけられます。

クエリセットはリクエストされたモデルを返す

User オブジェクトへのクエリで Django に MyUser オブジェクトを返させることはできません。 User オブジェク トへのクエリセットは User 型のオブジェクトを返すでしょう。プロキシオブ ジェクトの全体的なポイントは、元の User に依存しているコードは元のモデ ルを使い、追加したコードは拡張を使えるということです。(他のどんなコードも 追加のコードに依存しません。) プロキシモデルは User (または他のどんな モデルも) を自分で作成したもので置き換える手段ではないのです。

親クラスの制限

プロキシモデルは抽象クラスでないモデルクラスを 1 つだけ継承しなくてはなり ません。抽象クラスでないモデルクラスの多重継承をすることはできません。プロ キシモデルは、異なるデータベーステーブルの行へのコネクションを提供していな いからです。プロキシモデルはモデルフィールドを定義して いない 抽象モデル クラスならばいくつでも継承することができます。

プロキシモデルは親の抽象モデルクラスから Meta オプションを継承します。

プロキシモデルマネジャー

もしプロキシモデルにモデルマネジャーを指定しなかった場合、マネジャーはモデ ルの親から継承します。もしマネジャーを定義すれば、それがデフォルトになりま す。それでも、親クラスに指定されたマネジャーを使用することも出来ます。

上述した例を続けましょう。User モデルに問い合わせる時使われるデフォル トのマネジャーを、このように変更することが出来ます:

class NewManager(models.Manager):
    ...

class MyUser(User):
    objects = NewManager()

    class Meta:
        proxy = True

もしプロキシに新しいマネジャーを加えたい場合、デフォルトで存在するものと置 き換えることなく、付け加えることができます。 custom manager のドキュメントで述 べているテクニックを使うことができます。 新しいマネジャーを含んだベースクラスを作成して、主なベースクラスの後にそれ を継承しています:

# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
    secondary = NewManager()

    class Meta:
        abstract = True

class MyUser(User, ExtraManagers):
    class Meta:
        proxy = True

このテクニックを必要とすることはそんなにないでしょうが、可能です。

プロキシの継承とマネージされないモデルの違い

プロキシモデルの継承は、マネージされないモデルを作るのによく似ています。 マネージされないモデルは、モデルの Meta クラスに managed 属性を使うものです。 2 つの方法は全く同じものではないので、どちらを使った方がよいのか熟慮する 価値があります。

一つ目の違いは Meta.managed=False のモデルにはモデルフィールドを指定 できることです。 (そして実際、空のモデルを欲しいのでなければ、そうしなく てはいけませんね) 注意深く Meta.db_table を注意深く設定 することで、既存のモデルの影のような、しかし Python メソッドを追加する マネージされないモデルを作れます。しかしながら、それは新しく変更を加え る時に両方のコピーを同期させ続けなければならない脆弱なものになります。

もう 1 つの違いは、どのようにモデルのマネジャーが扱われるのかという、プ ロキシモデルにとってもっと大切な点です。プロキシモデルが意図しているの は、代理しているモデル自体のようにふるまうことです。だからデフォルトの マネジャーを含む親のモデルマネージャーを継承します。多テーブルのモデル を継承する場合、子は親からマネジャーを継承しません。エクストラフィールド が関係している場合は、カスタムマネジャーが適切であるとは限らないからです。 マネジャードキュメント にもっ と詳細な内容が載っています。

これら 2 つの特徴が実装されている時、これらを 1 つのオプションに押し込 めることが試みられました。そして継承の相互作用は一般的に、特にマネジャー は、 API をとても複雑にし、理解して使うことを難しくする可能性があると分 かりました。 2 つのオプションはあるケースでは必要だと分かりました。それ で、現在は 2 つのオプションが存在しています。

一般的なルールとして:

  1. 既存のモデルやデータベーステーブルをミラーしたくて、元のデータベース テーブルカラム全てを必要としない場合、 Meta.managed=False にして ください。このオプションはデータベースのビューとテーブルを Django の コントロール下に入れずにモデリングするのに役立ちます。
  2. Python でのみモデルの振る舞いを変えたいけれど、フィールドは同じものを 使いたい場合には Meta.proxy=True を使ってください。このように設定 すると、データがセーブされた時にプロキシモデルが元のモデルの構造を保 持したままの正確なコピーとなります。
多重継承

Python のサブクラスと同様、 Django のモデルも複数の親モデルクラスを継承でき ます。クラス内の名前解決には、 Python の通常の名前解決規則が適用されるので 注意してください。子クラスで特定の名前 (例えば Meta) を参照する場合、その名前を定義している最初のベースクラスの定義を使い、最初 に名前が見つかった時点で、それ以降同じ名前のオブジェクトの解決は行われませ ん。従って、複数の親クラスで別々に Meta クラスを定義 していても、最初のベースクラスの Meta だけが使われ、 それ以外は全て無視されます。

通常は、モデルの多重継承は必要ないでしょう。多重継承が便利なのは、主に特定 のフィールドやメソッドを追加するための ‘’mix-in’’ クラスを使う場合です。 継承の階層構造はできるだけ単純に、分かりやすくしておきましょう。さもないと、 子クラスで扱っている情報が、どの親クラスから来たか調べるために四苦八苦する はめになるでしょう。

フィールド名を「覆い隠すこと」は許可されていない

通常の Python クラスの継承では、子クラスで親クラスのどんな属性でもオーバラ イドすることができます。 Django では、 Field イ ンスタンスの属性は (少なくとも今のところは) オーバライドすることができませ ん。もし親クラスが author というフィールドを持っていたとして、親クラス を継承したどんなクラスの中でも新しく author フィールドを作ることはでき ません。

親モデルのフィールドをオーバライドすることは、インスタンスの初期化 (どのフ ィールドが Model.__init__ で初期化されるかを指定すること) 、そしてシリ アライズを困難なものにします。これらは通常の Python クラスの継承が全 く同じように扱う必要のない特徴です。つまり Django のモデル継承と Python ク ラスの継承との違いは決定的なものなのです。

この制限は Field インスタンスである属性のみに適 用されます。通常の Python 属性は望み通りにオーバーライドできます。また、こ の制限は Python が見ている属性の名前のみに適用されます。自身の手でデータベ ースのカラム名を指定するのなら、多テーブルの継承向けに同じカラム名を先祖と 子のクラス両方に登場させることもできます (二つの異なるデータベーステーブル のカラムとして)

先祖のモデルの中にあるモデルフィールドをオーバーライドしようとすると、 Django は FieldError を生じさせます。

クエリを生成する

revision-up-to:17812 (1.4)

データモデル を作成したら、次はデータベースからデー タを取り出す必要があります。このドキュメントでは、モデルから利用できるデー タベース抽象化 API と、オブジェクトを生成、取得、更新する方法について説明し ます。モデルの照合オプションの詳細は データモデルリファレンス を参照してください。

このリファレンスでは、以下のような Poll アプリケーションを参考に話を進めま す:

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __unicode__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

    def __unicode__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateTimeField()
    mod_date = models.DateTimeField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __unicode__(self):
        return self.headline
オブジェクトの生成

Django では、データベーステーブル上のデータを Python オブジェクトで表現する ために、モデルクラスがデータベーステーブルを表現し、クラスのインスタンスが テーブル上のレコードを表現するという直感的なシステムを使っています。

オブジェクトを生成するには、キーワード引数を使ってモデルクラスのインスタン スを生成し、 save() メソッドを呼び出して データベースに保存します。

モデルクラスは Python パス上のどこからでも import でき、期待通りに動作しま す (わざわざこのような説明をするのは、以前のバージョンの Django ではモデル の import 方法がかなり風変わりだったからです)。

モデルが mysite/blog/models.py というファイルで定義されているとすると、 オブジェクトの作成は以下の例のようになります:

>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

この操作によって、背後では INSERT SQL 文が実行されます。 Django はユー ザが明示的に save() を呼び出すまで データベースを操作しません。

save() メソッドには戻り値がありません。

See also

save() には高度な使い方のための オプションがありますが、ここでは解説しません。詳しくは save() のドキュメントを参照してください。

ワンステップでオブジェクトを生成して保存するには create() メソッドを使って ください。

オブジェクトへの変更を保存する

すでにデータベース上にあるオブジェクトへの変更を保存するには save() を使います。

Blog インスタンス b5 がすでにデータベース上にあるとすると、以下の例 は b5 の名前を変更して、データベース上のレコードを更新します:

>> b5.name = 'New name'
>> b5.save()

この例では、背後で UPDATE SQL 文が実行されています。 Django は明示的に save() を呼び出すまでデータベースを操作しません。

ForeignKeyManyToManyField の保存

ForeignKey フィールドの更新は、 通常のフィールドへの変更と同じです。すなわち、 適切な型のオブジェクトを代入して保存すると、フィールドの値を更新でき ます。この例では、Entry のインスタンス entry``の ``blog アトリビュートを更新しています。:

>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

ManyToManyField の更新は少し違います。 リレーションにレコードを一つ追加したい場合は add() メソッドを使いま す。この例では、 entry オブジレェクトに、 Author インスタンスの joe を加えています:

>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

複数のレコードを ManyToManyField に一度で加えたい 場合、 add() に、 複数の引数をくるんで呼び出せばいいのです。:

>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)

間違った型のオブジェクトを外部キーに代入したり add() したりすると Django はエラーを出します。

オブジェクトの取得

オブジェクトをデータベースから取得するには、モデルクラスのマネジャ Manager を介してクエリセット QuerySet を構築します。

QuerySet はデータベース上にある オブジェクトの集まりを表現しています。 QuerySet`には、集合を指定パラメタに従って 絞り込むための条件である*フィルタ (filter)* を複数個持たせられます。 SQL 用語でいえば、クエリセットは ``SELECT` 文であり、 フィルタは WHERELIMIT のような限定節にあたります。

QuerySet はモデル の:class:~django.db.models.Manager から取得します。モデルには最低一つの Manager があり、デフォルトでは objects という 名前がついています。マネジャにはモデルクラスから直接アクセスしてください:

>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
    ...
AttributeError: "Manager isn't accessible via Blog instances."

Note

(「テーブルレベル」の操作と「レコードレベル」の操作を分離させるため、マ ネジャはモデルのインスタンスではなくモデルクラスだけからアクセスできる ようになっています。)

モデル内での QuerySets の主なソースは Manager です。 マネジャは、データベースオブジェクト上の全てのオブジェクトを表す「ルートの」 クエリセットであるかのように振舞います。例えば、初期クエリセットである Blog.objects には、データベース上の全ての Blog オブジェクトが入っています。

全てのオブジェクトの取得

テーブルからオブジェクトを取得する最も単純な方法は全てのオブジェクトを取得 するというものです。全オブジェクトを取得するには、 Managerall() メソッドを使って下さい:

>>> all_entries = Entry.objects.all()

all() メソッドはデータベース上の全ての オブジェクトを表現する QuerySet() を返します。

(Entry.objects がクエリセットを返すというのなら、なぜ単に Entry.objects と書かないのでしょうか?それは、ルートのクエリセットであ る Entry.objects が特別扱いされていて、値評価できないようになっているか らです。 all() メソッドは、値評価 できる クエリセットを返します。

フィルタを使ってオブジェクトを取り出す

Manager によって提供される、 いわゆるルート QuerySet を使えば、 データベーステーブル上の全てのオブジェクトを表せます。とはいえ、 通常は全オブジェクトの集合からサブセットだけを取り出したいことでしょう。

サブセットを作成するには、フィルタ条件を追加して、 初期 QuerySet を リファインする必要があります。 QuerySet の洗練には、主に二つの方法があります:

filter(**kwargs)
指定した照合パラメタに一致するオブジェクトの集合を表現する、新たな QuerySet を返します。
exclude(**kwargs)
指定した照合パラメタに一致 しない オブジェクトの集合を表現する、 新たな QuerySet を返します。

照合パラメタ (上の関数定義における **kwargs) は、後述の フィールドの照合 で解説するフォーマットにせねばなりません。

例えば、 2006 年のブログエントリを表す QuerySet を取得するには、以下のように filter() を使います:

Entry.objects.filter(pub_date__year=2006)

(Entry.objects.all().filter(...) のように、 all() を使わなくてもよいことに注意して下さい。 all() を使っても問題なく動作しますが、 all() が必要となるのはルートクエリセットから 全てのオブジェクトを取り出したい場合だけです。)

フィルタの連鎖

QuerySet をリファインした結果は、 それ自体 QuerySet になります。 従って、リファイン操作は連鎖させられます。例えば:

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.now()
... ).filter(
...     pub_date__gte=datetime(2005, 1, 1)
... )

この例では、データベースの全てのエントリを表す初期クエリセットに対し、 filter() をかけた後に exclude() を実行し、さらにもう一つ filter() をかけています。最終的に得られるのは、 “What” で始まるヘッドラ インのうち、 January 1, 2005 から今日までの間に公開されたエントリです。

フィルタしたクエリセットは一意なオブジェクトである

QuerySet のリファインを行うと、 その都度新たなクエリセットを得ます。新たな QuerySet は 以前のクエリセットになんら縛られていません。リファイン操作のたびに、 別個の独立した QuerySet が作成され、個別に保存したり、 再利用したりできます。

例えば:

>> q1 = Entry.objects.filter(headline__startswith="What")
>> q2 = q1.exclude(pub_date__gte=datetime.now())
>> q3 = q1.filter(pub_date__gte=datetime.now())

これら 3 つのクエリセットは別個のものです。最初はヘッドラインが “What” で始 まる全てのエントリの入ったベースのクエリセットです。二つ目のクエリセットは、 最初のクエリセットのサブセットであり、 pub_date の値が現在時刻よりも大 きいものを排除します。三つ目のクエリセットも最初のクエリセットのサブセット で、 pub_date の値が現在時刻よりも大きいものだけを選択するようになって います。こうしたリファイン操作は、初期クエリセット (q1) に影響を及ぼし ません。

クエリセットは遅延評価される

クエリセットの評価は遅延型 (lazy) です。すなわち、 QuerySet の作成自体は データベース操作を引き起こしません。 QuerySet評価される までデータ ベースへのクエリを実行しないので、延々フィルタを重ねられます:

>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.now())
>>> q = q.exclude(body_text__icontains="food")
>>> print q

この例では、データベースに 3 度アクセスするように見えますが、実際には一度だ け、最後の行のとき (print q) しかアクセスしません。一般に、 QuerySet の結果は、その内容を「調べる」まで、データベースから取り出され ません。 QuerySet は、クエリの内容を調べたときにはじめて 値評価 され るのです。値評価がいつ行われているかは、 クエリセットはいつ評価されるのか で詳しく説明しています。

ゲットを使って一つのオブジェクトを取り出す

filter() は常に、 QuerySet を渡し、もし一つだけ オブジェクトがマッチするクエリ - のようなケースならば、 これは単一の要素を含んだ QuerySet です。

もし、クエリにマッチするのが一つだけだと分かるなら、 Managerget() メソッドが使えます。 これはオブジェクトを直接的に返します。:

>>> one_entry = Entry.objects.get(pk=1)

filter() と同じように、 クエリは get() と一緒に使えます、 もう一度 フィールドの照合 を見てください。

get() を使うのと、 filter()[0] でスライス するのとでは違いがあります。もし、クエリにマッチするものがなかった時、 get()DoesNotExist 例外を 送出します。この例外はモデルクラスの属性で、クエリは上にあるコードのように 動きます、もし Entry オブジェクトに 1 というプライマリーキーがないなら、 Django は Entry.DoesNotExist 例外を送出します。

同様に、 Django は get() クエリで 一つ以上のアイテムとマッチした場合にも例外を送出します。このようなケースでは、 MultipleObjectsReturned がモデルクラス自身の属性に反しています。

その他のクエリセットメソッド

ほとんどの場合、データベース上のオブジェクトを照合するには、 all() か、 get() filter() exclude() メソッドで事足ります。とはいえ、クエリセットがサポートしているメソッドは もっとたくさんあります。クエリセットメソッドの詳細な説明は、 クエリセット API リファレンス を参照してください。

クエリセットに制約を課す

クエリセットの返す結果を特定の個数に制限したい場合には、 Python の配列スラ イス表記を使います。これは SQL の LIMIT 節や OFFSET 節と等価です。

以下の例は、最初の 5 オブジェクトだけを返します (LIMIT 5 に相当します):

>>> Entry.objects.all()[:5]

以下の例は、 6 番目から 10 番目までのオブジェクトを返します (OFFSET 5 LIMIT 5 に相当します):

>>> Entry.objects.all()[5:10]

負の数でのインデクシングはサポートしていません。 (すなわち、 Entry.objects.all()[-1] このようなものです)

一般に、 QuerySet をスライスしても QuerySet を新たに生成して返すだけで、 クエリの評価は行いません。例外はスライス表記に「ステップ (step)」パラメタを 使ったときです。以下の例では、クエリを実際に実行し、最初の 10 オブジェクト 中から 1 つおきにオブジェクトを取り出したリストを返します:

>>> Entry.objects.all()[:10:2]

リストではなく 単一の オブジェクトを取得したい場合 (SELECT foo FROM bar LIMIT 1 のような場合) には、スライスではなく単純な インデクス指定を行います。以下の例はデータベースのエントリをヘッドラインに ついてアルファベット順に整列した後、最初の Entry を取得して返します:

>>> Entry.objects.order_by('headline')[0]

これはだいたい以下と同じになります:

>>> Entry.objects.order_by('headline')[0:1].get()

ただし、指定条件にマッチするオブジェクトがない場合、前者は IndexError, 後者は DoesNotExist を送出します。詳しくは get() のドキュメントを参照してください。

フィールドの照合

フィールドの照合操作によって、 SQL の WHERE 節の中身が決まります。フィー ルドの照合を行うには、 filter(), exclude() および get() といった QuerySet のメソッドのキーワード引数を指定します。

基本的に、照合のキーワード引数名は field__lookuptype=value のような形 式をとります (アンダースコアは二重です)。例えば:

>>> Entry.objects.filter(pub_date__lte='2006-01-01')

は、(大雑把にいって) 以下のような SQL 文に変換されます:

SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

これがどうして可能なのか

Python には、任意の名前と値をもった引数を受け取れる関数を定義する機能が あり、引数名とその値は実行時に評価されます。くわしい情報は公式の Python チュートリアルの キーワード引数 を参照してください。

照合の中でのフィールドの指定は、モデルフィールドの名前でなければいけませ ん。 ForeignKey の場合、例外ががありますが、 フィールドネームを _id というサフィックス(suffix)で指定できます。 このようなケースでは、バリューパラメータはフォーリンモデルの プライマリーキーの生の値を含みます。例えば::
>>> Entry.objects.filter(blog_id__exact=4)

照合時に無効なキーワード引数が渡されると、 TypeError が送出されます。

データベース API は 2 ダース近くの照合タイプをサポートしています。詳しいリ ファレンスは フィールド照合リファレンス を参照してく ださい。ここでは、よく使う照合タイプをいくつか示します:

exact

「厳密一致」です。例えば:

>>> Entry.objects.get(headline__exact="Man bites dog")

は、以下のような SQL を生成します:

SELECT ... WHERE headline = 'Man bits dog';

照合タイプを指定しなかった場合、つまり二重アンダースコアの入ったキー ワード引数を使わないかぎり、照合条件は exact とみなされます。

例えば、以下の二つの文は同じ意味です:

>>> Blog.objects.get(id__exact=14)  # Explicit form
>>> Blog.objects.get(id=14)         # __exact is implied

exact はよく使われるため、便宜的にこのような仕様にしています。

iexact

大小文字を区別しない一致です。以下のクエリ:

>>> Blog.objects.get(name__iexact="beatles blog")

は、 “Beatles Blog”, “beatles blog”, “BeAtlES blOG” といったタイト ルの Blog オブジェクトにマッチします。

contains

大小文字を区別する部分一致テストです。以下のクエリ:

Entry.objects.get(headline__contains='Lennon')

は、おおざっぱにいって以下の SQL に変換されます:

SELECT ... WHERE headline LIKE '%Lennon%';

'Today Lennon honored' には一致しますが、 'today lennon honored' には一致しないので注意してください。

大小文字を区別しない icontains もあります。

startswith, endswith
それぞれ、前方一致と後方一致です。大小文字を区別しない istartswithiendswith もあります。

ここに挙げたのはほんの一部です。全ての照合タイプの解説は フィールド照合タイプのリファレンス を参照してくださ い。

リレーションをまたいだ照合

Django では、背後で自動的に SQL JOIN を処理し、照合の際にリレーションを 「追跡」する、強力でありながら直感的な手段を提供しています。リレーションを またぐには、二重アンダースコアを使ってリレーションの張られたフィールドのフィー ルド名を指定します。リレーション間のスパンは、目的のフィールドに到達するま でいくらでも連鎖させられます。

以下の例では、 name'Beatles Blog' であるような BlogEntry エントリオブジェクト全てを取得します:

>>> Entry.objects.filter(blog__name__exact='Beatles Blog')

スパンは好きなだけ深く張れます。

リレーションのスパンは逆方向にも張れます。「逆方向の」リレーションを参照す るには、モデル名を小文字にした名前を使います。

以下の例では、 headline'Lennon' を含むような Entry を少なく とも一つ持つような全ての Blog オブジェクトを取得します:

>>> Blog.objects.filter(entry__headline__contains='Lennon')

複数のリレーションをまたいでフィルタを行っていて、中間のモデルにフィルタ条 件に合致する値が存在しないと、 Django はその値が空である (NULL が入って いる) だけの有意なオブジェクトとして扱います。つまり、エラーを送出しません。 例えば、以下のフィルタを考えましょう:

Blog.objects.filter(entry__author__name='Lennon')

(Author モデルは存在するものとします) entryauthor を空にし ていると、 Django は author が存在しないために name を辿れませんが、 エラーは送出せず、あたかも name も空であるかのように扱います。たいてい は、この仕様で期待通りに動きますが、 isnull を使うと、少し混乱をきたし ます。すなわち:

Blog.objects.filter(entry__author__name__isnull=True)

は、 authorname が空の Blog だけでなく、 entryauthor が空の Blog も返してしまうのです。後者のオブジェクトを返して 欲しくないのなら、以下のように書いてください:

Blog.objects.filter(entry__author__isnull=False,
        entry__author__name__isnull=True)
多値のリレーションをスパンする

ManyToManyFieldForeignKey の逆リレーションを使ってフィルタを行 う場合、 2 種類の異なるフィルタ方法があります。 Blog / Entry のリレー ション (Blog から Entry が 1 対多であるようなリレーション) を考えて みましょう。このようなモデルでは、例えばヘッドラインに ‘Lennon’ を含み、 かつ 2008 年に公開されたエントリを持つようなブログを検索したい場合があ るでしょう。あるいは、ヘッドラインに ‘Lennon’ を含むか、 あるいは 2008 年に公開されたエントリを含むようなブログの検索もあり得ます。複数のエン トリが一つの Blog に関連付けられているので、いずれのクエリも実行可能で すし、意味をなす場合があるでしょう。

ManyToManyField を使っている場合にも、 同じような状況が起こります。例えば、 Entrytags という名前の ManyToManyField があり、 “music”“band” というタグに関連付けられたエントリを検索したい場合や、 “music” タグのついた “public” 状態のエントリを取り出したい場合があるで しょう。

こうした状況をうまく扱うために、 Django には filter() および exclude() といった呼び出しがあります。 filter() に指定した条件は同時に適用され、 条件全てに一致する要素をフィルタします。 filter() を連続 して呼び出すと、通常はオブジェクトの集合により狭い制約をかけますが、多値リ レーションの場合、新たな制約は、前の filter() で絞り込んだリレーション 先をさらに絞り込むのではなく、前の filter() で絞り込んだリレーション先 にリンクしているリレーション元からリレーションを張られている全てのオブジェ クトに対して適用されてしまいます。

この話はちょっとややこしいので、例を挙げて解説しましょう。 ‘Lennon’ をヘッ ドラインに含み、 2008 年に公開されたエントリを含むブログを選択するには、以 下のように書きます:

Blog.objects.filter(entry__headline__contains='Lennon',
        entry__pub_date__year=2008)

一方、 ‘Lennon’ をヘッドラインに含むか、 あるいは 2008 年に公開された エンリを含むブログを検索するには、以下のように書きます:

Blog.objects.filter(entry__headline__contains='Lennon').filter(
        entry__pub_date__year=2008)

後者の例では、最初の filter() でクエリセットに制約をかけ、特定のエント リにリンクを持つブログだけを取り出しています。次の filter() では、取り 出した Blog オブジェクトから、 さらに 二つ目の条件に合うものに制約を かけています。従って、二つ目の filter() で制約をかけている対象の entry は、最初のフィルタで絞り込んだエントリと同じときもあれば、違うと きもあります。一つめの filter() でフィルタしているのはあくまでも Blog であって、 Entry ではないからです。

この挙動は、 exclude() にもあてはまります。 一つの exclude() に指定した条件は、 (多値リレーションの場合は) 各インスタンスに同時に適用されます。 filter()exclude() を連続して呼び出すと、 毎回違うリンク先集合を使ってフィルタを行ってしまう可能性があるのです。

フィルタはモデルのフィールドを参照できる

これまで与えられた例の中で、私たちはフィルタを同じモデルフィールドの バリューと比較してきました。しかし、モデルの値を同一のモデルの別のフィールドと 比較したい場合はどうしたらよいのでしょう?

Django は F() expressions をこの比較のために 提供しています。 F() のインスタンスはクエリの内側のモデルフィールドへの 参照として働きます。これらのは同じモデルインス タンスの異なる二つのフィールドを比較するクエリフィルターです。

例えば、これは pingbacks よりコメントが多い全てのブログエントリーのリスト を見つけるものです。 F() オブジェクトを使って、 pingback のカウント数 が参照できるようにすます。:

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

Django は追加や削除、かけ算、分割と剰余などの計算も F() オブジェクトで 使えるようにサポートしています。ブログエントリーの中で、 pingbacks よりコメン トが 二倍 以上あるものを抽出するには、クエリをこのようにします:

>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)

ブログエントリーの中で、 pingback 自体ののカウント数とコメント数よりも ブログエントリーの、エントリーのレーティングが小さい・少ないものにするには このようにクエリを書きます。:

>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

また、 F() オブジェクト内で離れた関係を表記するために、ダブルアンダー スコア表記を用いることができます。ダブルアンダースコア付きの F() オブジェクトは、ひもづいているオブジェクトへのアクセスが必要な 時のために存在しています。例えば、作者の名前がブログの名前と同じ 全てのエントリーを取り出そうとしたとき、このようなクエリが発行できます。

>>> Entry.objects.filter(authors__name=F('blog__name'))
リリースノートを参照してください

date と date/time フィールドは、 timedelta オブジェクト で足したり引いたりできます。これは発行されてから三日以上経た全ての エントリーを抽出するクエリです:

>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
pk 照合ショートカット

利便性のために、 Django には pk という照合形式があります。 pkprimary_key を表します。

Blog モデルの例では、主キーは id フィールドなので、以下の二つの文は 等価です:

>>> Blog.objects.get(id__exact=14) # 明示的な形式
>>> Blog.objects.get(id=14) # 暗黙で __exact を表す
>>> Blog.objects.get(pk=14) # pk は暗黙で id__exact を表す

pk__exact のクエリにしか使えないわけではありません。どのクエリ 用キーワードも pk と組み合わせてかまいません。 pk を使うと、モデル の主キーに対するクエリを表します:

# id が 1, 4 および 7 のブログエントリを取得する
>>> Blog.objects.filter(pk__in=[1,4,7])

# id > 14 の全てのブログエントリを取得する
>>> Blog.objects.filter(pk__gt=14)

pk による検索は join 越しでも行えます。 例えば、以下の二つの文は等価で す:

>>> Entry.objects.filter(blog__id__exact=3) # 明示的な形式
>>> Entry.objects.filter(blog__id=3)        # 暗黙で __exact を表す
>>> Entry.objects.filter(blog__pk=3)        # __pk は暗黙で __id__exact を表す
LIKE 文におけるパーセント記号とアンダースコアのエスケープ

LIKE を使う SQL 文になるようなフィールド照合メソッド (iexact, contains, icontains, startswith, istartswith, endswith, iendswith) では、 LIKE 文で使われる二つの特殊な文字、すなわちパーセ ント記号とアンダースコアを自動的にエスケープします。 (LIKE 文では、パー セント記号は任意の複数文字に対するワイルドカードを表し、アンダースコアは任 意の一文字に対するワイルドカードを表します。)

この機能によって、照合操作を直感的に行え、データベースの抽象化を守れます。 例えば、パーセント記号を含むようなエントリ全てを取得したければ、以下のよう にパーセント記号をそのまま使います:

>>> Entry.objects.filter(headline__contains='%')

Django はクオートの処理に気を配ってくれます。 SQL は以下のような感じになり ます:

SELECT ... WHERE headline LIKE '%\%%';

アンダースコアについても同じようなエスケープを行います。パーセント記号とア ンダースコアはいずれも透過的に処理されます。

キャッシュとクエリセット

データベースへのアクセスを最小限にとどめるため、 QuerySet 各々にはキャッシュがあります。 効率的なコードを書く上で、キャッシュのからくりを理解しておくのは重要なことです。

QuerySet が新たに生成された時点では、 キャッシュは空です。 QuerySet を 最初に評価したとき (すなわち、データベースへのクエリが最初に生じたとき)、 Django はクエリ結果をクエリセットオブジェクト内のキャッシュに保存し、明示的 にリクエストした結果だけ (例えば、 QuerySet‘s に対してイテレーション操作を する場合には、結果セットの最初の要素) を返します。それ以後は、 QuerySet‘ を再利用するとキャッシュ済みの結果を返します。

このキャッシュの挙動をよく覚えておいて下さい。というのも、 QuerySet を正しく扱わないと、 おもわぬところで手を噛まれるはめになるからです。例えば、以下の例では 二つの QuerySet を作成し、値を評価して、 すぐにクエリセットを捨ててしまっています:

>>> print [e.headline for e in Entry.objects.all()]
>>> print [e.pub_date for e in Entry.objects.all()]

そのため、全く同じデータベースクエリを二度実行し、データベースの負荷を倍加 させています。また、 Entry は二つのリクエストを処理する間にも追加された り削除されたりする可能性があるため、二つのリストには必ずしも同じデータベー スレコードが入っているとは限りません。

こうした問題を避けるには、 QuerySet を保存して再利用してください:

>>> queryset = Entry.objects.all()
>>> print [p.headline for p in queryset] # クエリセットを評価します。
>>> print [p.pub_date for p in queryset] # キャッシュの値を再利用します。
Q オブジェクトを使った複雑な照合

filter() などで複数の キーワード引数を指定してクエリを行うと、各々のキーワード引数の表す照合条件は 違いに “AND” で結ばれます。より複雑なクエリ (例えば OR を使ったクエリ) を 実行する必要がある場合には Q オブジェクトを使えます。

Q オブジェクト (django.db.models.Q) は、 複数のキーワード引数をカプセル化するために使われます。 キーワード引数は前述の 「フィールドの照合」で説明したものと同じです。

例えば、以下の Q オブジェクトは単一の LIKE クエリをカプセル化してい ます:

from django.db.models import Q
Q(question__startswith='What')

Q オブジェクトは &| といった演算子で組み合わせられます。二 つの Q オブジェクトを演算子で結ぶと、新たな Q オブジェクトを生成し ます。

例えば、以下の文は二つの question__startswith クエリを “OR” したものを 表す単一の Q オブジェクトを生成します:

Q(question__startswith='Who') | Q(question__startswith='What')

この Q オブジェクトは以下の WHERE 節と等価です:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

Q オブジェクトを &| で組み合わせれば、好きなだけ複雑なクエ リ文を作成できます。丸括弧を使ったグルーピングも可能です。また、 ~ 演算 子を使えば Q オブジェクトの「否 (nagate)」を取れるので、例えば以下のよ うに、通常のクエリと否のクエリの積をとれます:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

キーワード引数をとる照合関数 ( filter() exclude() get() など) には、複数の Q を固定引数として (名前なしの引数として) 渡せます。複数の Q オブジェクトを照合関数に渡した場合、それらは互いに “AND” で結ばれます。 例えば:

Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

は、だいたい以下のような SQL になります:

SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

照合関数は Q オブジェクトとキーワード引数を混ぜて使えます。照合関数に渡 した全ての引数は (キーワード引数も Q オブジェクトも) 互いに “AND” で結 ばれます。ただし、 Q を指定する場合はどのキーワード引数よりも前に指定せ ねばなりません。たとえば:

Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who')

は有効なクエリで、前の例と同じになりますが、以下の文:

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

は無効です。

See also

OR lookups examples の例には、 Q を使った Django のユニット テストがあります。

オブジェクトの比較

二つのモデルオブジェクトを比較するには、標準の Python 比較演算子、すなわち 二重等号符: == を使います。背後では二つのモデルオブジェクト間の主キー値 が比較されます。

上の Entry の例では、以下の二つの文は等価です:

>>> some_entry == other_entry
>>> some_entry.id == other_entry.id

モデルの主キーが id という名前でなくても問題はありません。どのような名 前であれ、比較は常に主キーを使って行われます。例えば、モデルの主キーのフィー ルド名が name であれば、以下の二つの文は等価です:

>>> some_obj == other_obj
>>> some_obj.name == other_obj.name
オブジェクトの削除

削除用のメソッドには、 便宜的に delete() という名前が付いてます。 このメソッドはオブジェクトをただちに削除し、戻り値を返しません:

e.delete()

複数のオブジェクトの一斉削除も可能です。 QuerySet には QuerySet() の全てのメンバを削除します。

例えば、 pub_date が 2005 年の Entry オブジェクトを全て削除するには 以下のようにします:

Entry.objects.filter(pub_date__year=2005).delete()

忘れてならないのは、この処理はできる限り SQL レベルで行われるため、 delete() メソッドが全てのインスタンスの delete() メソッドを呼ぶとは 限らないということです。モデルクラス上で delete() をカスタマイズしてい て、オブジェクトを削除するときに呼び出したいのなら、 QuerySetdelete() メソッドで一括削除するのではなく、直接 ( QuerySet の各要素を 取り出して逐次 delete() を呼ぶなどして) 「手動で」インスタンスを削除せ ねばなりません。

Django は、オブジェクトを削除する際に、通常 SQLでいう ON DELETE CASCADE 制約をエミュレートします。すなわち、 削除対象のオブジェクトを指すような外部キーを持つ全てのオブジェクトも 同時に削除されるのです。:

b = Blog.objects.get(pk=1)
# 次の命令は、 Blog と Blog を指す Entry 全てを削除してしまいます。
b.delete()
このカスケードの挙動は ForeignKeyon_delete 引数を通して変えることが出 来ます。

delete()QuerySet のメソッドにすぎず、 Manager 自体には公開 されていないので注意してください。これは誤って Entry.objects.delete() を実行して 全ての エントリを削除してしまわないようにするための安全機構で す。本当に全てのオブジェクトを削除 したい のなら、以下のように明示的に全 てのオブジェクトを表すクエリセットをリクエストしてください:

Entry.objects.all().delete()
モデルインスタンスをコピーする

モデルインスタンスをコピーする組み込みのメソッドは提供されていません、 ですが全てのフィールドとバリューを同一にした新しいインスタンスを簡単に作成する ことができます。最もシンプルなケースでは、 pkNone にすることができ ます。ブログの例を使うと:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # post.pk == 1

blog.pk = None
blog.save() # post.pk == 2

継承を使うと、もう少し複雑になってしまいます。 Blog のサブクラスについて 考えてみましょう。:

class ThemeBlog(Blog):
    theme = models.CharField(max_length=200)

django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme = 'python')
django_blog.save() # django_blog.pk == 3

継承の働きによって、 pkid を None にしなくてはいけません:

django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

このプロセスはひもづいているオブジェクトをコピーしません。もし、関係性のあるも のもコピーしたいのならば、もう少し多めにコードを書かなければいけません。 EntryAuthor に many to many フィールドを持っている場合:

entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors = old_authors # saves new many2many relations
複数のオブジェクトを一度に更新する

QuerySet が表現する全てのオブジェクトのある フィールドに特定の値を指定したいような場合があります。 update() メソッドを使えば、 この処理を実現できます。例えば:

# pub_date が 2007 の全エントリのヘッドラインを更新する
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

この操作を行えるのは、リレーションフィールドでないフィールドか、 ForeignKey のフィールドのみです。また、フィールドに指定する値はハードコー ドされた Python の値でなければなりません (つまり、その時の何らかのフィール ド値は直接セットできません)。 ForeignKey のフィールドを更新するには、以下のように、 新たなモデルインスタンスを作成して渡します:

>>> b = Blog.objects.get(pk=1)

# 全てのエントリをこの blog に属させる
>>> Entry.objects.all().update(blog=b)

update() メソッドは (delete() と同様)、即座に適用され、値を返しませ ん。また、更新対象のクエリセットには、モデルの主のテーブル一つに対してし かアクセスできないという制限があります。フィルターは関係するテーブルに 使えますが、アップデートできるのはモデルのメインテーブルのカラムだけです。:

>>> b = Blog.objects.get(pk=1)

# この blog に属している全ての headline をアップデートします。
>>> Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same')

update() メソッドは SQL 文に直接変換されるので注意してください。このメ ソッドは、フィールド値の直接更新を大量一括実行するためのものです。 モデルの save() メソッドを呼出さないので、 ( QuerySet の呼び出しに連動している) pre_savepost_save といった シグナルは発生しません。QuerySet 内の全ての要素に対して save() メソッドを適用したければ、 単に全要素に渡って save() を呼び出してください:

for item in my_queryset:
    item.save()

モデルの中の別フィールドの値によって別フィールドをアップデートするには F() objects も使えます。これは、特に 最新の値によってカウンタを増加させたい場合に使えます。例えば、 pingback のカウント数をブログのエントリごとに増加させるには:

>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

しかしながら、 F() はフィルタ内と EXCLUDE 句とは 違うところもあり、 アップデートに F() オブジェクトを使った時には JOIN が使えません。 つまり、アップデートされるモデル内で参照できるフィールドのみを使えるという ことです。もし、 JOIN を F() オブジェクトで使おうとした場合、 FieldError が生じます。:

# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))
生 SQL へのフォールバック

Django のデータベースマッパで扱うには複雑すぎる SQL 文を書かねばならないよ うな場合には、生の SQL 文実行モードを使えます。Django は生 SQL 文をクエリ とうる方法を二つ持っています。 素の SQL クエリを直接実行する を見てください。

生 raw-SQL 文実行モードの使い方としてお勧めなのは、そのようなクエリ文を実行 するモデルのカスタムメソッドやカスタムマネジャのメソッドを実装するというも のです。 Django はモデルレイヤでデータベースクエリを記述するように 何ら要求してはいません が、このアプローチをとることで、データアクセスのた めのロジックを一箇所にまとめられるので、コード組織化の観点からスマートにな ります。詳しい説明は topics-db-sql を参照してください。

最後に、 Django のデータベースレイヤは単にデータベースへの一つのインタフェー スに過ぎないということに注意しておきましょう。データベースには他のツールや プログラム言語、データベースフレームワークを介してアクセスできます。データ ベースについて Django 固有の何かがあるわけではないのです。

集約(Aggregation)

revision-up-to:17812 (1.4)

このトピックは Djangoのデータベース抽象API において個別のオブジェクトの作成、取得、削除を行うDjangoのクエリの使い方で 説明されています。しかし、オブジェクトのコレクションの合計を取ったり、 集約 したりすることで引き出された値を取得しなければならないことがあります。 このトピックガイドはDjangoクエリを使って集約値がどうやって生成されたり 返されたりするのかを解説します。

このガイドでは以下のモデルを使います。これらモデルはオンライン書店の一連 の在庫を管理するために使われます。

class Author(models.Model):
   name = models.CharField(max_length=100)
   age = models.IntegerField()
   friends = models.ManyToManyField('self', blank=True)

class Publisher(models.Model):
   name = models.CharField(max_length=300)
   num_awards = models.IntegerField()

class Book(models.Model):
   isbn = models.CharField(max_length=9)
   name = models.CharField(max_length=300)
   pages = models.IntegerField()
   price = models.DecimalField(max_digits=10, decimal_places=2)
   rating = models.FloatField()
   authors = models.ManyToManyField(Author)
   publisher = models.ForeignKey(Publisher)
   pubdate = models.DateField()

class Store(models.Model):
   name = models.CharField(max_length=300)
   books = models.ManyToManyField(Book)
チートシート

お急ぎですか? 上のモデルを使った場合の一般的な集約クエリは以下のようになり ます

# 書籍の合計数
>>> Book.objects.count()
2452

# publisher=BaloneyPress の場合の書籍の合計数
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73

# 全書籍の平均価格
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

# 最も高額な書籍
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}

# 出版社ごとの書籍数を "num_books"属性で
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
[<Publisher BaloneyPress>, <Publisher SalamiPress>, ...]
>>> pubs[0].num_books
73

# トップ5の出版社を書籍数の多い順に
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323
QuerySetに対して集約を生成する

Djangoでは集約を生成するのに2つの方法が用意されています。最初の方法は、 全 QuerySet に対して合計値を生成する物です。例えば、売り出されている 全ての書籍の平均値を計算したい場合などです。Djangoのクエリ文法では、全書籍 セットを表現するには以下の方法が用意されています

>>> Book.objects.all()

必要なのはこの QuerySet に含まれるオブジェクトに関しての合計値を計算 する方法です。これは aggreagte() 句を QuerySet に加えることで 行われます。

>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

all() はこの例では冗長なので、 次のように簡素化できます

>>> Book.objects.aggregate(Avg('price'))
{'price__avg': 34.35}

aggregate() 句への引数は計算したい集約値を表します – この例では、 Book モデルの price フィールドの平均になります。 利用可能な集約関数の一覧は QuerySet リファレンス にあります。

aggregate()QuerySet の最後の句になります。それが呼び出されると、 name-valueペアの辞書が返されます。nameは集約値に対する識別子です;valueは 計算された集約値です。 name はフィールド名と集約関数より自動的に生成されます。 集約値の名前を手動で指定したいのであれば、集約句を指定する際にその名前を 指定します

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}

1つ以上の集約を生成したいのであれば、 aggregate() 句に別の引数を 追加するだけです。よって、全書籍の最高の定価と最低の定価を知りたいのであれば 、次のクエリを発行します

>>> from django.db.models import Avg, Max, Min, Count
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
QuerySetの各アイテムを集約する(注釈付け)

サマリ値を生成する2つ目の方法は QuerySet の各オブジェクトに対して 個別のサマリを生成することです。例えば、書籍の一覧を取得しているとすれば、 それぞれの書籍に寄稿している著者が何名いるのかを知りたいこともあるでしょう。 各書籍は Author に対して many-to-many の関係を持っています;この QuerySet での関連をサマリ化できます。

オブジェクトごとのサマリは annotate() 句を使うことで生成することができ ます。 annotate() が指定されると、 QuerySet の各オブジェクトは 指定された値で注釈付け( annotate )されます。

これらの注釈付け ( annotation ) 構文は aggregate() 句で使われる物と 全く同じです。 annotate() への各引数は計算される注釈付けを意味します。 例えば、Bookを著者数で注釈付けするには

# 注釈付けされるクエリセットを組み立てる
>>> q = Book.objects.annotate(Count('authors'))
# クエリセットの最初のオブジェクトを取得
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# クエリセットの2番目のオブジェクト取得
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1

aggregate() と同様に, 注釈(annotation)の名前は集約関数の名前と集約される フィールド名で自動的に作成されます。 注釈付けを指定する時にエイリアス名を 指定すると、このデフォルトの名前をオーバーライドすることができます

>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1

aggregate() とは違って, annotate() は最終句では ありませんannotate() 句の出力は QuerySet です; この QuerySet は他の QuerySet オペレーションによって修正可能です。 filter()order_by などが使え、また別の annotate() 呼び出しさえも可能です。

Joinと集約

ここまではクエリを受けるモデルに帰属するフィールドに対して集約することに 関して扱ってきました。しかし、集約したい値が、クエリを受けるモデルの関連 するモデルに属することもあります。

集約関数の中で集約したいフィールドを指定するときには、 Djangoではフィルタ において、関連フィールド参照するときにつかわれるのと同じ 二重アンダースコア表記 を使うことことができ ます。 そうすれば Django は関連した値を取得して集約するために必要な任意 のテーブルをjoinしてくれます。

例えば、それぞれのお店で提供されている書籍の価格帯を見つけるにはこのような 注釈付けを行うと良いでしょう

>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

こうすると Django は Store モデルを取得し( many-to-many 関連を通じて ) Bookモデルと join し、書籍のpriceフィールドに関して注釈付けして最小値と 最大値を生成してくれます。

同じルールは aggregate() 句にも適用されます。あるお店で売られている 任意の書籍の最安値と最高値を知りたいのであれば、次のような集約を使います:

>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))

Joinのチェーンは必要なだけ深くできます。例えば、売り出し中の任意の書籍の 中で最も若い著者の年齢を取得するには、次のようなクエリを発行します

>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
集約と他のQuerySet句
filter()exclude()

集約はフィルタに参加させることができます。 任意の filter() (あるいは exclude() ) を通常のフィールドに適用すれば、集約したいオブジェクトの 制約をさせる効果が得られます。

annotate() 句を使うと、 フィルタは注釈付け計算したいオブジェクト を制限できるようになります。例えば、次のクエリを使うと表題が “Django”で 始まる全ての書籍に関しての注釈付けリストを生成することが出来ます

>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))

aggregate() 句を使うと集約計算したいオブジェクトを制限できるように なります。例えば、次のクエリを使うと表題が”Django”で始まる全ての書籍の 平均価格を生成することができます

>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))
注釈付けに対するフィルタリング

注釈付けされた値もフィルターできます。 他のモデルフィールドと同じ方法で、 filter()exclude() 句で注釈付けのエイリアスを使うことで来ます。

例えば、作家が一人以上いる書籍の一覧を生成するには、次のクエリを発行します:

>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

このクエリは注釈付けした結果セットを生成して、その注釈付けを元にした フィルタを生成します。

annotate() 句と filter() 句の順番

annotate() 句と fitler() 句 の両方を含んだ複雑なクエリを開発している のであれば、これらの句がその QuerySet に適用される順番に付いて特に注意 を払うべきです。

クエリに annotate() 句が適当される時は、 注釈付けはそれが要求された時点 までのクエリの状態に関して注釈付けが行われます。 これが実際に意味することは、 filter()annotate() は代替可能な 操作ではないということです – すなわち、クエリ間で結果の差があるということ です。

>>> Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)

と次のクエリです

>>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))

2つのクエリは少なくとも1つの良い書籍(すなわち評価が 3.0を超えた書籍) があるPublisher のリストを返します。しかし、最初のクエリの注釈付けでは 出版社が出版する全ての書籍の総数を返します;2番目のクエリは注釈付けされた 件数での良い本の一覧を返します。最初のクエリでは、フィルタの前に注釈付け されるので、フィルタは注釈付けに何の影響も与えません。2番目のクエリでは フィルタが注釈付けの前に行われるので、注釈付け計算が行われるべきオブジェクト をフィルタが制限します。

order_by()

注釈付けはソート順( ordering ) に基づいて行うことが可能です。 order_by() 句を定義すると、提供する注釈付けがクエリの中で annoteate() 句の部分と して定義したエイリアスを参照することができます。

例えば、書籍に寄稿した著者の数で書籍の QuerySet を並べ替えるには 次のクエリを使います。:

>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
values()

本来、注釈付けはオブジェクト毎に行われるのが基本です。– 注釈付けされる QuerySet は元の QuerySet での各オブジェクトに対して1つの結果を 返します。しかし、結果セットで返されるカラムを制限するために values() が使われると、注釈付けの評価方法少しかわります。元の QuerySet の 各結果に対して注釈付けの結果を返すのではなく、 元の結果が values() 句 で指定されたフィールドのユニークな組み合わせに対してグループ化されます。 そして注釈付けが各ユニークグループに対して行われます;注釈付けはグループの 全てのメンバに対して計算されるのです。

例えば、それぞれの著者によって書かれた書籍の平均評価を見つけるために著者の クエリを次のように考えてください

>>> Author.objects.annotate(average_rating=Avg('book__rating'))

これはデータベースにある各著者に対して、彼らの平均書籍評価で注釈付けして 1つの結果を返します。

しかし、 values() 句を使えばこの結果は少し異なります

>>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))

この例では、 著者が name でグループ化されるので、各 ユニークな 著者の name に対して注釈付けされた値のみが取得されます。つまり、同じ名前の二人の著者が 居ると、1つのクエリの結果に結合されます;平均は二人の著者で書かれた本の平均 として計算されるのです。

annotate() 句と values() 句の順番

filter() 句を使うと、 クエリに適用される annotate() 句と values() 句の順番が重要です。 values() 句が annotate() 句の前にあれば、注釈 付けは values() 句で表現されたグループ化を使って計算されます。

しかし、 annotate() 句が values() の前にあると、注釈付けは全 クエリセットに対して生成されます。この場合、 values() 句は出力に生成 されるフィールドのみを制限します。

例えば、 values() 句と annotate() 句の順番を前の例で反対にすると:

>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

これは各著者に対する1つのユニークな結果を生成します;しかし、著者の name と average_ration の注釈付けのみが出力データに返されます。

average_ragint が返されるべき値リストに明示的に含まれていることも注目 すべきです。これは values() 句と annotate() 句の順番の為に必要なの です。

もしも values() 句が annotate() 句の前にあるならば、注釈付けは 自動的に結果セットに加えられます。しかし、 values() 句が annotate() 句の後に加えられるのであれば、明示的に注釈付けカラムを含める必要があります。

デフォルトのソート順あるいは order_by() との相互作用

クエリセットの order_by() で示されたフィールド(もしくはモデルに対する デフォルトのソート順で使われるフィールド)はたとえ values() の呼び出しで 指定されていなかったとしても出力データを選択する時には使われます。 このような追加フィールドは”like” な結果を一緒にグループかするのに使われたり、 同じ結果行を分離するのに使われたりします。 特に、件数を数えるときに使われます。

例えば、 このようなモデルがあるとします

class Item(models.Model):
    name = models.CharField(max_length=10)
    data = models.IntegerField()

    class Meta:
        ordering = ["name"]

ここでの重要な部分は、 name フィールドのデフォルトのソート順です。 もしも、ユニークな data の値が何回現れるのかを数えたいのであれば、この ようにしてもいいでしょう

# 警告: 完全に正しい訳ではない!
Item.objects.values("data").annotate(Count("id"))

...こうすると共通の data の値で Item オブジェクトがグループ化され て、各グループの id の値の件数が数えられます。ただし、完全には動作しま せん。 name のデフォルトのソート順はグルーピングでも有効ですので、この クエリはユニークは (data,name) ペアでグループされてしまい、求める結果 とは異なってしまいます。代わりに次のクエリセットを作るべきです

Item.objects.values("data").annotate(Count("id")).order_by()

...このクエリでは任意のソート順を明らかにしています。 つまり data を使って 有害な影響なく並べ替えることができました。クエリの中でその役目を既に果たして いるからです。

この振る舞いは distinct() に対する クエリセットのドキュメントに書かれていることと同じで、一般ルールも同じです。 :普通は結果の中で追加フィールドが役目を果たすことはないので、ソート順を明確 にしないならば、少なくとも values() 呼び出しで選択するこれらのフィールドに 対して制限するようにしないといけません。

Note

Django が無関係のカラムを取り除いてくれないのはなぜなのか?と思うのも もっともです。主な理由は distinct() や他の場所との一貫性です : Django は指定されたそーと順制約を 決して 取り除くことはしません ( 他のメソッドの振る舞いも変更することができません。 API の安定性 にあるポリシーに違反するからです )

注釈付けを集約する

注釈付けの結果を集約することもできます。 aggregate() 句を定義する時は その集約は、クエリの annotate() の部分で定義された任意のエイリアスを 参照することができます。

例えば、書籍とに著者数の平均を計算したいのであれば、まず書籍の集合に対して 著者数で注釈付けし、それから注釈フィールドを参照して著者数を集約します。

>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}

マネジャ

revision-up-to:17812 (1.4) unfinished
class Manager

マネジャ (Manager) とは、データベースクエリ操作を Django モデルに提供し ているインタフェースです。 Django アプリケーション内のモデルは全て少なくと も一つマネジャを備えています。

マネジャクラス Manager の動作はデータベース API ドキュメントの クエリを生成する で説明していますが、この節ではマネジャの挙動をカス タマイズするためのモデルオプションについて具体的に触れます。

マネジャの名前

デフォルトでは、 Django は全ての Django モデルクラスに object という名 前で Manager オブジェクトを追加します。ただし、 objects をフィール ド名として使いたい場合や、マネジャに objects 以外の名前をつけたい場合に は、モデルごとに名前を変えてやる必要があります。クラス中でマネジャの名前を 変更するには、 models.Manager() 型のクラス属性を定義します。例えば:

from django.db import models

class Person(models.Model):
    #...
    people = models.Manager()

このようにすると、 Person.people.all()Person オブジェクトのリス トを提供し、 Person.objects の参照は AttributeError 例外を送出します。

カスタムマネジャ

ベースクラスの Manager クラスを拡張して、モデル中でカスタムのマネジャを インスタンス化すれば、モデルでカスタムのマネジャを使えます。

マネジャをカスタマイズする理由は大きく分けて二つあります。一つはマネジャに 追加のメソッドを持たせたい場合、もう一つはマネジャの返す初期 QuerySet を変更したい場合です。

追加のマネジャメソッド定義

モデルに「テーブルレベル」の機能を持たせたい場合、マネジャへのメソッドは良 い方法です。 (「行レベル」の機能を持たせたい、例えばモデルオブジェクトの個々 のインスタンスに影響する関数を実装したい場合には、カスタムのマネジャメソッ ドではなく モデルのメソッド を使って下さい。)

カスタムのマネジャメソッドは何を返してもかまいません。 QuerySet を返さ なくてもよいのです。

例えば、以下のカスタムマネジャでは、 with_counts() というメソッドを提供 しています。このメソッドは全ての OpinionPoll オブジェクトからなるリスト を返しますが、その際に集約クエリの結果である num_responses という追加の 属性を追加します:

class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        cursor = connection.cursor()
        cursor.execute("""
            SELECT p.id, p.question, p.poll_date, COUNT(*)
            FROM polls_opinionpoll p, polls_response r
            WHERE p.id = r.poll_id
            GROUP BY 1, 2, 3
            ORDER BY 3 DESC""")
        result_list = []
        for row in cursor.fetchall():
            p = self.model(id=row[0], question=row[1], poll_date=row[2])
            p.num_responses = row[3]
            result_list.append(p)
        return result_list

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()

class Response(models.Model):
    poll = models.ForeignKey(Poll)
    person_name = models.CharField(max_length=50)
    response = models.TextField()

この例では、 OpinionPoll.objects.with_counts() を使うと、 num_responses 属性を備えた OpinionPoll オブジェクトのリストを返しま す。

この例でもう一つ注意して欲しいのは、マネジャメソッドが自分の属しているモデ ルクラスを取り出すために self.model にアクセスできるという点です。

初期 QuerySet の変更

マネジャのベースの QuerySet は、システム上の全てのオブジェクトを返しま す。例えば、以下のモデル:

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

では、 Book.objects.all() とすると、データベース上の全ての books を返し ます。

Manager.get_query_set() メソッドをオーバライドすれば、 Manager のベー スの QuerySet をオーバライドできます。 get_query_set() は必要なプロ パティを備えた QuerySet を返さねばなりません。

例えば、以下のモデルには 二つの マネジャがあります。片方は全てのオブジェ クトを返し、もう一方は Roald Dahl の書いた本だけを返します:

# まず Manager のサブクラスを定義します。
class DahlBookManager(models.Manager):
    def get_query_set(self):
        return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')

# 次に Book モデルに明示的にフックします。
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # デフォルトマネジャ。
    dahl_objects = DahlBookManager() # Dahl 縛りのマネジャ。

このモデル例では、 Book.objects.all() はデータベース上の Book を全て返 しますが、 Book.dahl_objects.all() は Roald Dahl の書いた本だけを返しま す。

get_query_set()QuerySet オブジェクトを返すので、もちろん filter()exclude() をはじめ全ての QuerySet メソッドを使えま す。従って、以下のような文を実行できます:

Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()

この例はもう一つ興味深いテクニックの存在を示しています。それは同じモデルで 複数のマネジャを使えるということです。モデルには、好きなだけマネジャのイン スタンスをアタッチできます。この手法を使うと、よく利用するフィルタをモデル に簡単に実装できます。

例えば:

class MaleManager(models.Manager):
    def get_query_set(self):
        return super(MaleManager, self).get_query_set().filter(sex='M')

class FemaleManager(models.Manager):
    def get_query_set(self):
        return super(FemaleManager, self).get_query_set().filter(sex='F')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
    people = models.Manager()
    men = MaleManager()
    women = FemaleManager()

この例では、 Person.men.all(), Person.women.all(), Person.people.all() といったクエリを発行できるようになっており、期待通 りの結果を得られます。

カスタムのマネジャオブジェクトを使う場合、 Django がモデル内に最初に見つけ たマネジャ (モデルに定義した順番で最初のマネジャ) は特別扱いされるというこ とに注意してください。 Django はクラス内で最初に見つけたマネジャを「デフォ ルトの」マネジャにし、このデフォルトマネジャを ( dumpdata を含 む) あちこちでモデルのマネジャとして使います。ですから、 get_query_set() のオーバライドによって、扱いたいオブジェクトを取り出せ なくなるような羽目に陥らないように、デフォルトマネジャの選択には細心の注意 を払ってください。

カスタムマネジャとモデルの継承

クラスの継承とモデルのマネジャの関係は、お互い完全にしっくりきているわけで はありません。マネジャはたいていあるクラス固有のものなので、サブクラスでマ ネジャを継承するのは必ずしもよいアイデアとはいえないからです。また、最初に 宣言されたマネジャが*デフォルトマネジャ* になることから、デフォルトマネジャ の制御が重要になってきます。そこで、ここでは、 Django がカスタムマネジャと モデル継承 をどのように扱うか説明します:

  1. 抽象ベースクラスでないクラスで定義されたマネジャは、他のクラスに継承 されません 。非抽象ベースクラスのマネジャを再利用したければ、 子クラスで明示的に宣言してください。この種のマネジャは、たいていマネ ジャを定義しているクラス固有のもので、(デフォルトマネジャとして) 継 承すると思わぬ結果を招くことがあるからです。そのため、子クラスに自動 的に継承されないのです。
  2. 抽象ベースクラスのマネジャは、通常の Python の名前解決規則 (name resolution order, 子クラスの名前が他の名前をオーバライドする、 親クラスリストの最初の親クラスの名前から順に参照する、など) に基づい て、常に子クラスに継承されます。抽象ベースクラスは、子クラスに共通し て持たせたい情報やふるまいを保持するためのクラスだからです。共通のマ ネジャの定義は、共通の情報として親クラスに置くのが適切です。
  3. デフォルトマネジャは、そのクラスで最初に宣言されたマネジャクラスか、 最初に見付かった抽象ベースクラスのデフォルトマネジャです。明示的なデ フォルトマネジャの設定がなければ、 Django の通常のデフォルトマネジャ を使います。

これらのルールはモデルのグループに対してカスタムマネジャのコレクションを インストールしたいときに十分なフレキシビリティを提供しています。 例えば、このようなベースクラスがあるとします

class AbstractBase(models.Model):
    ...
    objects = CustomManager()

    class Meta:
        abstract = True

サブクラスでこれを直接使うのであれば、 ベースクラスでマネジャを定義して いないので objects がデフォルトのマネジャ になります。

class ChildA(AbstractBase):
    ...
    # このクラスは CustomManager をデフォルトマネジャとして持っています

AbstractBase から継承するけれども別のマネジャをデフォルトで用意する のであれば、子クラスにデフォルトマネジャを提供することが出来ます:

class ChildB(AbstractBase):
    ...
    # 明示的なデフォルトマネジャ
    default_manager = OtherManager()

ここで、 default_manager がデフォルトです。 objects マネジャは 引き続き利用できます。というのは継承されているからです。デフォルトとして 使われないというだけです。

この例の最後として、子クラスにマネジャを追加したいけれども、 AbstractBase のデフォルトを引き続き利用したいとします。子クラスに 新しいマネジャを直接追加することができません。 というのはそうするとデフォルトを上書いてしまい、抽象基底クラスの全ての マネジャを明示的に含めなければならなくなるからです。 解決法は他の基底クラスにマネジャを追加して、デフォルトの後に継承階層に 含めることです

class ExtraManager(models.Model):
    extra_manager = OtherManager()

    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    ...
    # デフォツオマネージャは CustomManagerですが、
    # OtherManagerも"extra_manager"属性として利用可能です。
実装する際に考慮すること

カスタム Manager にどんな機能を追加したにせよ、 Manager インスタンスのシャロウコピー (shallow copy)を可能にしなければなりませ; すなわち、以下のコードは正しく動く必要があります

>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)

Django はある種のクエリの最中にマネジャをシャロウコピーさせます; もしも Manager がコピーされないのであればそのようなクエリは失敗します。

これはほとんどのカスタムマネジャにとっては問題とはならないでしょう。 自分の Manager に簡単なメソッドを追加するだけであれば、 うっかり Manager インスタンスをコピーさせないようにしてしまう、という ことはあり得ないでしょう。しかし、 オブジェクトの状態を管理するために Manager オブジェクトの __getattr__ やその他のプライベートメソッド をオーバーライドするのであれば、 Manager がコピーされる能力に影響を 与えないようにするべきです。

自動マネジャタイプの制御

このドキュメントはDjangoがマネジャクラスを作る際にすでに何度も触れられて きました: 関連オブジェクトにアクセスする ための デフォルトマネジャ と “プレーン” マネジャ、です。 Djangoの実装においては、一時的なプレーン マネジャが必要な箇所がいくつかあります。これらの自動的に作成されるマネジャ は通常は django.db.models.Manager のインスタンスです。

この章では “自動マネジャ( automatic manager )” という用語を、 Django が モデルマネジャを自動的に作成してくれる、という意味で使います。 – マネジャ無しのモデルのデフォルトマネジャの場合もありますし、関連オブジェクト にアクセスする際に一時的に使われたりもします。

このデフォルトクラスが正しい選択では無い場合もあります。 Django自体では 提供されていない django.contrib.gis アプリケーションに1つの例があります。 データベースと円滑に相互作用するためには、特殊なクエリセット (GeoQuerySet)を使わなくてはならないので、 全ての gis モデルは特殊な マネジャクラス(GeoManager)を使わなくてはいけません。 このような特殊なマネジャを必要とするモデルは自動マネジャが作成されるときには 必ず同じマネジャクラスを使う必要があります。

Django にはカスタムマネジャ開発者に対して、モデルのデフォルトマネジャが作成 される際には必ず自分の作成したマネジャクラスが自動マネジャとして使われるよう に指定できるような方法が用意されています。これは、マネジャクラスに use_for_related_fields 属性を設定することで実現されます

class MyManager(models.Manager):
    use_for_related_fields = True

    ...

この属性がモデルの デフォルト マネジャに設定される(デフォルトマネジャだけ がこのような状況になり得ます)と、Django はクラスに対して自動的にマネジャを 作成する必要になると必ずこのクラスを使うようになります。 それ以外の場合は、 django.db.models.Manager が使われます。

歴史的ノート

使われる目的を考えると、この属性名(use_for_related_fields)は 少し変です。元々は、この属性は関連フィールドのアクセスだけに使われる ようなマネジャを制御していましたのでこういう名前になったのです。 コンセプトが明確になるにつれてますます広い用途で使われるようになり ましたが、名前は変更されていません。これは第一に既存のコードが 将来の Django のバージョンに置いても 継続し動作する ようにするためです。

自動マネジャインスタンスに使えるように正しくマネジャを書く

上記の django.contrib.gis の例ですでに触れたように、 use_for_related_fields は主にカスタム QuerySet サブクラス を返さなければならないマネージャのための機能です。 あなたのマネジャでこの機能を提供する場合、いくつか覚えておかなければ ならないことがありますので、このセクションで示します。

この種のマネジャのサブクラスでは結果をフィルタしてはいけない

自動マネジャが使われる一つの理由は、他のモデルから関連したオブジェクトに アクセスする場合です。この場合、Djangoはフェッチしようとしているモデル の全てのオブジェクトを見る必要があるので、 参照される 全て が取得 される必要があるのです。

もしも get_query_set() メソッドをオーバーライドして、列をフィルター すれば Django は正しくない結果を返します。これをやってはいけません。 get_query_set() の結果をフィルタするマネジャは自動マネジャとしては ふさわしくありません。

素の SQL クエリを直接実行する

revision-up-to:17812 (1.4) unfinished

モデルクエリAPI では十分には役に立たない場合、 逆戻りして素のSQLを書くことができます。Djangoでは素のSQLクエリを実行する のに2つの方法があります:Manager.raw() を使って、 素のクエリを実行する ことでモデルインスタンスを返すか、あるいは モデル層全体を避けて カスタムSQLを直接実行する ことができます。

素のクエリを実行する
リリースノートを参照してください

マネージャーメソッド raw() を使って素のSQLクエリを実行し、モデルインスタンス を返すことができます。:

Manager.raw(raw_query, params=None, translations=None)

このメソッドは素のSQLクエリを受け取りそれを実行し、 RawQuerySet インスタンスを返します。 この RawQuerySet インスタンスを通常の QuerySet の様に列挙してオブジェクトインスタンスを返すことができます。

例を使うと最も簡単に説明できます。次のようなモデルがあるとします:

class Person(models.Model):
    first_name = models.CharField(...)
    last_name = models.CharField(...)
    birth_date = models.DateField(...)

このようなカスタムSQLを実行したとします

>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
...     print p
John Smith
Jane Jones

もちろんこの例はあまりエキサイティングではありません – Person.objects.all() を実行しているのと全く同じです。しかし、 raw() は他のオプションを提供 していてそれを使うと非常にパワフルです。

モデルテーブル名

Person テーブルの名前はこの例では何処から来たのか?

デフォルトでは Djanog はデータベーステーブル名をモデルの “アプリケーションラベル(app label)” – manage.py startapp で 使われた名前です – とモデルのクラス名を間にアンダースコアを入れて 連結させて作ります。仮定した例では、 Person モデルは myapp アプリケーションに存在するので、 myapp_person となります。

詳細は db_table オプションに関するドキュメントで 確認してください。そこにはデータベーステーブル名を手動で設定する 方法も書かれています。

Warning

.raw() に渡されたSQL文はチェックされません。DjangoはそのSQL文が データベースから行セットを返すことを期待しますが、そうするために 何かをしてくれる訳ではありません。クエリが行を返さないのであれば、 (おそらく意味不明な)エラーとなります。

クエリフィールドをモデルフィールドに対応付けする

raw() はクエリの中のフィールドをモデルのフィールドに自動的に対応付け します。

クエリにおけるフィールドの並び順は関係ありません。言い換えると、以下の 2つのクエリは同じように動きます:

>>> Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')
...
>>> Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person')
...

対応付けは名前で行われます。つまり、クエリの中でSQLの AS 句を使って フィールドをモデルフィールドに対応付けすることができます。よってもしも他の テーブルに Person のデータを持っているのであれば、 Person インスタンス に簡単に対応付けさせることができます

>>> Person.objects.raw('''SELECT first AS first_name,
...                              last AS last_name,
...                              bd AS birth_date,
...                              pk as id,
...                       FROM some_other_table''')

名前が一致するのであれば、モデルインスタンスは正常に作成されます。

また translations 引数を raw() に渡すことでクエリの フィールドをモデルフィールドに対応付けすることも出来ます。 これは、クエリのフィールド名をモデルのフィール名に辞書で対応付けさせる 方法です。例えば、上のクエリはこのように書き換えることが出来ます:

>>> name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
>>> Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
インデックス参照

raw() ではインデックスを使うことができるので、最初の結果だけが欲しいの であればこのように書くことができます:

>>> first_person = Person.objects.raw('SELECT * from myapp_person')[0]

けれども、インデックス化とスライス化はデータベースレベルでは行われません。 データベースに巨大な Person オブジェクトがあるのならば、SQLレベルで クエリに制限をかける方がより効率的です

>>> first_person = Person.objects.raw('SELECT * from myapp_person LIMIT 1')[0]
モデルフィールドを遅延評価する

フィールドは省略することができます

>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')

このクエリで返される Person オブジェクトは遅延評価されるモデル インスタンスとなります ( defer() を参照 )。つまり、クエリで省略されたフィールドが必要であればロードすること が出来るということです。例えば:

>>> for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'):
...     print p.first_name, # オリジナルのクエリで取得される
...     print p.last_name # 必要なときに取得される
...
John Smith
Jane Jones

外見上は下の名前も名字も同じクエリで取得されたように見えます。しかし、この例 では実際は3つのクエリが発行されます。raw()クエリでは下の名前のみが取得され ます – 名字は print される時に必要に応じて取得されます。

省略出来ないフィールドが1つだけあります – プライマリキー フィールドです。 Djangoはプライマリキーを使ってモデルインスタンスを識別 するので、素のクエリには常に含まれている必要があります。プライマリキーを 入れるのを忘れると InvalidQuery 例外が投げられます。

アノテーションの追加

モデルに定義されないフィールドを含んだクエリを実行することも出来ます。 例えば、 PostgreSQL’s age() function を使ってデータベースで計算した 年齢の人々を一覧することができます

>>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
>>> for p in people:
...     print "%s is %s." % (p.first_name, p.age)
John is 37.
Jane is 42.
...
raw() にパラメータを与える

クエリをパラメータ化したいのであれば、 raw()params 引数を渡す ことが出来ます

>>> lname = 'Doe'
>>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])

params はパラメータリストです。 クエリ文字列の中で %s プレースホルダーを使います(これはデータベース エンジンに関わらず同じです); それらは params リストからのパラメータで 置き換えられます。

Warning

rawクエリでは文字列書式化を使うな!

上のクエリをこの様に書き換えたくなるかも知れません:

>>> query = 'SELECT * FROM myapp_person WHERE last_name = %s' % lname
>>> Person.objects.raw(query)

してはいけません

params リストの使用は SQL injection attacks から完璧に 守られます。この攻撃は攻撃者がデータベースに任意のSQLと投入するための よく知られている手法です。もしも文字列の補完挿入を使うと、早晩SQL インジェクションの餌食になるでしょう。 params リストを使う限りは 攻撃から守られます。

カスタムSQLを直接実行する

時々 Manager.raw() でさえも十分でないことがあります: モデルに明確に マップさせることの出来ないクエリや、 UPDATE . INSERT , DELETE を 直接実行する必要なこともあるでしょう。

このようなケースでは、モデル層全体を通過してデータベースを直接操作すること が常に可能です。

django.db.connection というオブジェクトがデフォルトのデータベー ス接続を 表現して、 django.db.transaction がデフォルトのトランザクションを表現して います。このデフォルトのデータベース接続を使うには、 まず connection.cursor() を呼び出してカーソルオブジェクトを取得します。 次いで cursor.execute(sql, [params]) を呼び出して SQL を実行した後、 cursor.fetchone()cursor.fetchall() を読んで結果行を返します。 データを変更する操作を行った後には、必ず transaction.commit_unless_managed() を呼び出して、変更をデータベースに commit してください。クエリが単にデータを取得するだけの操作なら、 commit は 必要ありません。 例を示しましょう:

def my_custom_sql():
    from django.db import connection, transaction
    cursor = connection.cursor()

    # データを変更する操作なので、 commit が必要
    cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
    transaction.commit_unless_managed()

    # データを取得するだけの操作なので commit は不要
    cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
    row = cursor.fetchone()

    return row

複数のデータベースを使っているのであれば django.db.connections を使って 目的のデータベース接続(とカーソル)を取得することができます。 django.db.connections は辞書ライクなオブジェクトでエイリアスを使って 特定のデータベース接続を取得することができます:

from django.db import connections
cursor = connections['my_db_alias'].cursor()
# ここにコードを書く...
transaction.commit_unless_managed(using='my_db_alias')

デフォルトでは Python DB API がフィールド名無しの結果を返しますが、 これは値の list であって dict でないということです。 少しだけパフォーマンスを犠牲にすれば次のようにして結果を dict で 返すことができます

def dictfetchall(cursor):
    " カーソルの全ての行を辞書として返す "
    desc = cursor.description
    return [
        dict(zip([col[0] for col in desc], row))
        for row in cursor.fetchall()
    ]

2つの違いを例として示します:

>>> cursor.execute("SELECT id, parent_id from test LIMIT 2");
>>> cursor.fetchall()
((54360982L, None), (54360880L, None))

>>> cursor.execute("SELECT id, parent_id from test LIMIT 2");
>>> dictfetchall(cursor)
[{'parent_id': None, 'id': 54360982L}, {'parent_id': None, 'id': 54360880L}]
トランザクションと素のSQL

素のSQL呼び出しをするときは、 Djangoは現在のトランザクションを自動的に ダーティとしてマークします。これらの呼び出しを含むトランザクションが 正確に閉じたことを確認する必要があります。 詳細に関しては、 Djangoのトンランザクション処理に求められるもの を参照してください。

リリースノートを参照してください

Django 1.3より前では、 素のSQL呼び出しを使う際、 transaction.set_dirty() を使って手動で トランザクションを ダーティーとマークする必要がありました。

接続とカーソル

connectioncursorPEP 249 で説明されている Python DB-API 標準をほとんど実装しています (ただし トランザクション処理 を除く)。 Python DB-APIに精通していないのであれば cursor.execute() のSQL文は SQL文にパラメータを直接追加するのではなく、プレースホルダーとして "%s" を使うということを知っておいてください。 このテクニックを使うのであれば、使用されているデータベースライブラリは必要に 応じて 自動的にパラメータをクオートしたりエスケープしたりします。 (また、Djangoは "%s" を求めているのであって SQLite Python バインディング で使われる "?" では ありません 。これは一貫性とわかりやすさのためです) 。

データベーストランザクションの管理

revision-up-to:17812 (1.4)

Django はトランザクションをサポートしているデータベース向けに、トランザクショ ン管理を制御する方法をいくつか提供しています。

デフォルトのトランザクション処理

Django のデフォルトの挙動では、組み込みのデータ変更に関わるモデル関数を呼び 出したときにはいつでも自動的に commit を行います。例えば、 model.save()model.delete() を呼び出すと、変更は即座にコミットされます。

これはほとんどのデータベースにおける自動コミット設定とほとんど同じ挙動です。 すなわち、ユーザがデータベースへの書き込みを必要とするような操作を行うと、 Django はすぐに INSERT/UPDATE/DELETE 文を実行し、次いで COMMIT を実行します。暗黙のロールバックは行いません。

HTTP リクエストとトランザクションを結び付ける

TransactionMiddleware を介してリクエストとレスポンスのフェイズにトラン ザクションを結び付けるというものです。

このトランザクション処理は次のように行われます: まず、リクエスト処理の開始 時にトランザクションを開始します。レスポンスを問題なく生成できたら、全ての トランザクションをコミットします。ビュー関数が例外を送出したら、ロールバッ クを起こします。

この機能を有効にするには、 TransactionMiddleware ミドルウェアを MIDDLEWARE_CLASSES 設定に追加します:

MIDDLEWARE_CLASSES = (
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.transaction.TransactionMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
)

ミドルウェアの配置順はとても重要です。トランザクションミドルウェアは呼び出 されるビュー関数だけでなく、後続のミドルウェアモジュール全てに影響します。 従って、セッションミドルウェアをトランザクションミドルウェアの後ろに配置す ると、セッションの生成はトランザクションの一部に入ってしまいます。

いくつかのキャッシュミドルウェアの例外は: CacheMiddleware, UpdateCacheMiddleware, FetchFromCacheMiddleware は影響を受けませ ん。データベースキャッシュを使用しているときでも、 Django のキャッシュミドル ウェアは独自のデータベースカーソルを使います。(内部的に独自のデータベース コネクションとマッピングされています。)

ビュー内でトランザクションを管理する
トランザクションを管理するコンテキストマネージャーは Django 1.3 で 新しくなりました。

ほとんどのユーザにとって、非明示的なリクエストベースのトランザクションは素 晴らしい働きをすることでしょう。しかしながら、トランザクションの管理方法を より詳細に制御したい場合、 django.db.transaction の中の関数群を使って 関数ごと、またはコードブロックベースごとにトランザクションを管理してください。

これらの関数は、下記で詳細に説明しています。二つの方法で使えます。:

  • 特定の関数の デコレーター としての一例は:

    from django.db import transaction
    
    @transaction.commit_on_success
    def viewfunc(request):
        # ...
        # this code executes inside a transaction
        # ...
    
  • コンテキストマネージャー として特定のブロックに使うには

    from django.db import transaction
    
    def viewfunc(request):
        # ...
        # this code executes using default transaction management
        # ...
    
        with transaction.commit_on_success():
            # ...
            # this code executes inside a transaction
            # ...
    

両方のテクニックは、 Python の全てのサポートバージョンで使えます。 しかしながら、 Python 2.5 では、 with 文を使う際には from __future__ import with_statement をモジュールの先頭で 加えなければ行けません。

Note

下記ではビュー関数を例に取ってはいますが、以下に述べるデコレータと コンテキストマネージャーはトランザクションを扱う必要がある全ての コードで使用することが出来ます。

autocommit()

ビュー関数のトランザクションの挙動を、グローバルな設定に関係なく Django の デフォルトの挙動にスイッチするには、 autocommit デコレータを使います。

例えば:

from django.db import transaction

@transaction.autocommit
def viewfunc(request):
    ....

@transaction.autocommit(using="my_other_database")
def viewfunc2(request):
    ....

viewfunc() の中では、 model.save()model.delete() 、その他デー タベースに書き込みを行う全ての関数でトランザクションを commit します。 viewfunc2() は同じ挙動になるでしょう、ですが "別のデータベース" コネクションにです。

commit_on_success()

commit_on_success デコレータを使うのは、関数内の全ての処理にわたるトラン ザクションを使えます:

from django.db import transaction

@transaction.commit_on_success
def viewfunc(request):
    ....

@transaction.commit_on_success(using="my_other_database")
def viewfunc2(request):
    ....

関数の実行に成功すると、 Django はそれまでの全ての作業を commit します。関 数が例外を送出すると、 Django はトランザクションを rollback します。

commit_manually()

トランザクションを完全に管理したい場合には、 commit_manually デコレータ を使います。このデコレータは Django にユーザが自分でトランザクションを管理 しようとしていることを知らせます。

commit()rollback() を行わずにデータを変更した場合は TransactionManagementError 例外を送出します。

手動のトランザクション管理は以下のようになります:

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):
    ...
    # commit/rollback を好きなタイミングで行えます
    transaction.commit()
    ...

    # ただし、自分でちゃんとやっておくのを忘れないように!
    try:
        ...
    except:
        transaction.rollback()
    else:
        transaction.commit()

@transaction.commit_manually(using="my_other_database")
def viewfunc2(request):
    ...
トランザクションを操作する必要性
リリースノートを参照してください

Django はリクエストの完了より前にオープンなトランザクションを閉じなくてはいけ ません。もし、 autocommit() (通常のコミットモード)を使用しているか、 commit_on_success() ならば、自動的にその処理が行われます。 しかし、手動でトランザクションを管理していると( commit_manually() デコレータを使用していれば)、トランザクションがコミットされるか、 ロールバックするかをリクエストが終了する前に確かめておかなければいけません。

これは全てのデータベースオペレーションに適用されます、書き込み処理だけでは ありません。データベースから読み取るだけだったとしても、トランザクションは リクエストが完了する前にコミットかロールバックされてなければいけません。

トランザクション管理をグローバルに無効化する方法

制御マニアの人は、 Django 設定ファイルで DISABLE_TRANSACTION_MANAGEMENTTrue に設定すれば、全ての自動トランザクション管理を無効にし、自分で トランザクションを管理できます。

この場合、 Django はいかなるトランザクション管理も行わなくなります。ミドル ウェアが非明示的にトランザクションを commit することはなくなり、自分でロー ル管理を行わねばなりません。さらに、何らかのミドルウェアで変更の commit を 自分で行わねばならなくなります。

従って、トランザクションの無効化は、自作のトランザクション制御ミドルウェア を実行したい場合や、本当に変わったことをやりたい場合向けです。ほとんどの状況では、 デフォルトの挙動かトランザクションミドルウェアで十分で、必要に応じ て特定の関数だけを変更すればよいでしょう。

セーブポイント

セーブポイントはトランザクションの中で、全てのトランザクションではなく、 トランザクションの一部をロールバックできるようにします。セーブポイントは PostgreSQL 8 や Oracle 、MySQL (バージョン 5.0.3 とそれより新しいもので InnnoDB をストレージエンジンに使っているもの)バックエンドで使えます。 他のバックエンドはセーブポイントを関数で提供します、しかしこれは空の オペレーションです – これは実際何もしません。

MySQL バックエンドでのセーブポイントは Django 1.4 からサポートされ始め ました。

セーブポイントは、通常の autocommit を Django で使っている限りこれは 役に立ちません。もし、 commit_on_successcommit_manually を 使っていると、それぞれの開かれたトランザクションは一連のデータベース処理として 構築され、コミットやロールバックを待ちます。もし生じた問題をロールバックしたい のならば、完全にトランザクションをロールバックします。セーブポイントは、 transaction.rollback() で動作する完全なロールバックを細かく 動作させられるようにします、

それぞれの関数は using 引数を取ります。これはこの処理が適用される データベースの名前にしてください。もし using 引数がなかったら、 "default" データベースが使われます。

セーブポイントは、トランザクションオブジェクトの三つのメソッドによって コントロールされています。:

transaction.savepoint(using=None)

新しいセーブポイントを作ります。これは、 “よい”状態にある トランザクションをマークするポイントです。

セーブポイント ID (sid)を返します。

transaction.savepoint_commit(sid, using=None)

最後のコミットか、セーブポイントが作られたときからの動作を含むように セーブポイントをアップデートします。

transaction.savepoint_rollback(sid)

トランザクションをセーブポイントかコミットされたところまで戻します。

セーブポイントを使った例は:

from django.db import transaction

@transaction.commit_manually
def viewfunc(request):

  a.save()
  # open transaction now contains a.save()
  sid = transaction.savepoint()

  b.save()
  # open transaction now contains a.save() and b.save()

  if want_to_keep_b:
      transaction.savepoint_commit(sid)
      # open transaction still contains a.save() and b.save()
  else:
      transaction.savepoint_rollback(sid)
      # open transaction now contains only a.save()

  transaction.commit()
MySQL でのトランザクション

MySQL を使っている場合、MySQL のバージョンと使っているテーブルの形式に応じ て、テーブルがトランザクションをサポートする場合としない場合があります。 (「テーブルの形式」とは、”InnoDB” や “MyISAM” などを指します。) このドキュ メントでは MySQL のトランザクションにまつわる説明はしませんが、 MySQL のサ イトには トランザクションに関する情報 が掲載されています。

MySQL 構成がトランザクションをサポート していない 場合、 Django は自動コ ミットモードで動作します。すなわち、 SQL 文は呼び出されたその場で実行され、 コミットされます。 MySQL 構成がトランザクションをサポートしている場合、 Django はこのドキュメントの説明通りにトランザクションを処理します。

PostgreSQL のトランザクションで例外処理を扱う

PostgreSQL のカーソルが例外を呼び出した時(典型的には IntegrityError )、 同じトランザクションの中の以後全ての SQL がエラーにより失敗します “トランザ クションブロックの終わりまでクエリは無視され、最新のトランザクションは 破棄されます”。一方で save() は PostgreSQL の例外処理とは似ていません、 そこではもっと進歩して慣用的なパターンがあり、ユニークフィールドでオブジェクト をセーブするようなものであったり、 force_insert/force_update フラグを使って セーブしたり、またはカスタム SQL の

この種のエラーからの回復方法はいくつかあります。

トランザクションロールバック

トランザクションを全て巻き戻すための最初のオプション例です:

a.save() # Succeeds, but may be undone by transaction rollback
try:
    b.save() # Could throw exception
except IntegrityError:
    transaction.rollback()
c.save() # Succeeds, but a.save() may have been undone

transaction.rollback() を呼ぶと、トランザクションをまるまる元に戻します。 コミットされてないデータベースへの手続きは全て失われます。この例では、 a.save() による変更は失われます、ですがこの手続きはエラーを出しません。

セーブポイント ロールバック

もし、 PostgreSQL 8 か、それより新しいものを使っていれば、 セーブポイント を使って データベースが動作する前、失敗するまえに戻せます、セーブポイントを アップグレードしたり設定することが出来ます。この方法は、オペレーションが 失敗した時に、一つ前の手続きにもどることができ、完全にトランザクションを 巻き戻さなくて済みます。:

a.save() # Succeeds, and never undone by savepoint rollback
try:
    sid = transaction.savepoint()
    b.save() # Could throw exception
    transaction.savepoint_commit(sid)
except IntegrityError:
    transaction.savepoint_rollback(sid)
c.save() # Succeeds, and a.save() is never undone

この例では、 a.save() は実行されておらず、 b.save() が 例外を発生させています。

データベースレベル オートコミット

PostgreSQL 8.2 かそれより新しいと、進歩したオプションが PostgreSQLを実行 するためにあります。 データベースレベル オートコミット です。 もしこのオプションを使うと、恒常的にトランザクションが開かなくなり、 例外を捕まえた後でも処理を継続できるようになります。:

a.save() # succeeds
try:
    b.save() # Could throw exception
except IntegrityError:
    pass
c.save() # succeeds

Note

これは、 オートコミットデコレータ と同じではなく、データベースレベルでのオートコミットを使う時に、それは データベースのトランザクションは全くありません。 オートコミット でコレーッタはトランザクションを使い、データベースが変化する処理が生じた 時に、自動的にそれぞれのトランザクションをコミットします。

マルチデータベース (Multiple databases)

revision-up-to:17812 (1.4)
リリースノートを参照してください

このトピックガイドでは、 Django がマルチデータベースをサポートしたことについて 述べられています。他の多くの Django ドキュメントでは単一のデータベースを扱う ことを想定しています。マルチデータベースを扱いたいなら、いくつか追加の手順を行う 必要があります。

データベースを定義する

Django で複数のデータベースを扱うための最初のステップは、使用したいデータベース サーバについて Django に伝えることです。これには DATABASES 設定を使い ます。データベースへのエイリアスを設定し、 Django 全体にわたって特定のデータ ベースを参照できるようにします。特定のデータベースとの接続を設定する辞書です。 内部の辞書の設定については、すべて DATABASES ドキュメントで述べられて います。

データベースにはどんなエイリアスでも設定できます。しかし、 default は特別な 意味を持っています。Django は他のデータベースが一切選ばれていない場合に、 default によってデータベースを使います。 default データベースが無い 場合は、常に使うデータベースの指定に気をつけてください。

以下の例は settings.py の一部で、 2 つのデータベースを定義しています。デフォ ルトが PostgreSQL データベースで、 users は MySQL データベースを呼び出し ます:

DATABASES = {
    'default': {
        'NAME': 'app_data',
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'USER': 'postgres_user',
        'PASSWORD': 's3krit'
    },
    'users': {
        'NAME': 'user_data',
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'mysql_user',
        'PASSWORD': 'priv4te'
    }
}

DATABASES 設定に定義していないデータベースにアクセスすると、 django.db.utils.ConnectionDoesNotExist 例外が送出されます。

データベースの同期

syncdb 管理コマンドは 1 度に 1 つのデータベースを操作します。デフォ ルトでは default データベースが操作されます。 --database 引数 を与えることで、 syncdb に他のデータベースを同期するよう指示できます。例における 全データベースの全モデルを同期するには、以下を実行する必要があります:

$ ./manage.py syncdb
$ ./manage.py syncdb --database=users

どのアプリケーションも特定のデータベースに同期させたくないときは、 database router を定義できます。これによって 特定のモデルに利用制限を設けられます。

他にも、粒度の小さい同期の制御をしたいなら、特定アプリケーションの sqlall の出力のすべてか一部を、直接データベースプロンプトにパイプし ます。こんなかんじです:

$ ./manage.py sqlall sales | ./manage.py dbshell
他の管理コマンドを使う

データベースを扱う他の django-admin.py コマンドは、 syncdb と 同じように動作します。今までと同じく、データベースを制御するための --database を使用します。は一度に 1 つのデータベース上でのみ動作し ます。

自動データベースルーティング

マルチデータベースを扱う最も簡単な方法は、データベースルーティングスキーム (a database routing scheme) を設定することです。デフォルトのルーティングスキーム は、オブジェクトが元のデータベースに ‘ベットリ (sticky)’ なままであることを保証 します (例えば foo データベースから取ってきたオブジェクトが、同じ foo データベースに保存されるということ) 。デフォルトのルーティングスキームは、データ ベースが特定されていないと、全クエリを default データベースに返します。

デフォルトのルーティングスキームを有効にする必要はありません。 ‘設定する必要なく (out of the box)’ 全 Django プロジェクトで提供されています。より面白いデータ ベースの割り当てをしたいなら、個別のデータベースルータを定義、導入しましょう。

データベースルータ

データベースルータは、 4 つのメソッドを提供するクラスです。

db_for_read(model, **hints)

model タイプのオブジェクトの読み込み操作に使われるデータベースを提示 します。

データベース操作においてデータベースを選ぶ助けになる追加の情報を提示できる なら、 hints 辞書に提供します。妥当なヒントの詳細は こちら に書かれています。

提示するものが無い場合は None を返します。

db_for_write(model, **hints)

Model タイプのオブジェクトの書き込みに使われるデータベースを提示します。

データベース操作においてデータベースを選ぶ助けになる追加の情報を提示できる なら、 hints 辞書に提供します。妥当なヒントの詳細は こちら に書かれています。

提示するものが無い場合は None を返します。

allow_relation(obj1, obj2, **hints)

obj1 、 obj2 間のリレーションが許可されている場合 True 、リレーションが 許可されていない場合 False 、 ルータに設定がない場合 None を返します。単なる 検証の操作です。外部キー、多対多の操作において、リレーションが 2 オブジェク ト間で許可されているかどうか判別するために使用されます。

allow_syncdb(db, model)

modeldb で与えられたデータベースと同期されるべきかどうかを判別し ます。モデルが同期されるべきとき True 、 そうでないとき False 、 ルータに 設定がない場合 None を返します。このメソッドは与えられたデータベース上に モデルが存在するかどうかを判別するのに使えます。

ルータはこれら すべて のメソッドを提供する必要はありません。 1 つかそれ以上は 省略されるでしょう。あるメソッドが省略されれば、 Django は該当のチェックを行う とき、そのルータをスキップします。

ヒント

データベースルータから受け取ったヒントによって、与えられたリクエストをどのデータ ベースで受け取るべきかを決めます。

今のところ、提供される唯一のヒントは instance です。実行中の読み込み、もしく は書き込み操作に関連づけられたオブジェクトのインスタンスです。これは保存された インスタンスか、多対多のリレーションに追加されたインスタンスになります。場合に よっては、インスタンスヒントは全く提供されません。ルータはインスタンスヒントの 存在をチェックします。ルーティングの振る舞いを変えるためにヒントが使われるべきか どうか判断します。

ルータを使う

データベースルータは DATABASE_ROUTERS 設定によって導入されます。 この設定ではクラス名のリストを定義します。マスタールータ (django.db.router) に使われるべきルータを指定します。

マスタールータは Django のデータベース操作に使われます。これはデータベースに処理 を割り当てるために使われます。クエリがどのデータベースを使うべきか知る必要がある ときは常にマスタールータが呼び出されます。その際モデルと (有効な場合は) ヒントが ルータに提供されます。データベースが提示されるまで、 Django は順番に各ルータを 試します。提示されない場合、現在のヒントインスタンスの _state.db を試し ます。ヒントインスタンスが提供されないか、インスタンスがデータベースステートを 保持していない場合、マスタールータは default データベースを割り当てます。

例の狙い!

この例はデモンストレーションを意図しています。ルータインフラストラクチャ (the router infrastructure) が、データベースの使い方を変えるために、どの ように使えるかのデモンストレーションです。デモンストレーションに際して、 いくつかの複雑な問題はあえて無視します。

myapp 中のモデルのうちのどれかが other データベース外のモデルとの関係 を含んでいれば、この例は動きません。 Cross-database relationships では、 Django が今のところ扱えない参照整合性の問題を紹介しています。

記載されているマスター/スレーブ構成にも欠陥があります。応答の遅延を扱う上で、 なんの解法も提供しません (つまり、クエリ不一致によって、スレーブに伝播させる ための書き込みに時間が取られるからです) 。

実際どんな意味があるのでしょう? myappother データベースに存在し、 その他のモデルは masterslave1slave2 間の、マスター/スレーブ 構成に存在して欲しいとしましょう。これを実現するには、 2 つのルータが必要です:

class MyAppRouter(object):
   """myapp アプリケーションのモデルの全データベース操作を制御するルータ"""

   def db_for_read(self, model, **hints):
       "myapp モデルの全操作を 'other' に向ける"
       if model._meta.app_label == 'myapp':
           return 'other'
       return None

    def db_for_write(self, model, **hints):
       "myapp モデルの全操作を 'other' に向ける"
       if model._meta.app_label == 'myapp':
           return 'other'
       return None

    def allow_relation(self, obj1, obj2, **hints):
       "myqpp 中のモデルが含まれている場合、全リレーションを許可"
       if obj1._meta.app_label == 'myapp' or obj2._meta.app_label == 'myapp':
           return True
       return None

    def allow_syncdb(self, db, model):
       "myapp アプリケーションが 'other' DB にのみあることを確かなものとする"
       if db == 'other':
           return model._meta.app_label == 'myapp'
       elif model._meta.app_label == 'myapp':
           return False
       return None

class MasterSlaveRouter(object):
   """単純なマスター/スレーブ構成を設けるルータ"""

   def db_for_read(self, model, **hints):
       "全読み込み操作をランダムなスレーブに向ける"
       return random.choice(['slave1','slave2'])

    def db_for_write(self, model, **hints):
       "全書き込み操作をマスターに向ける"
       return 'master'

    def allow_relation(self, obj1, obj2, **hints):
       "DB プール内で 2 オブジェクト間のリレーションを許可"
       db_list = ('master','slave1','slave2')
       if obj1._state.db in db_list and obj2._state.db in db_list:
           return True
       return None

    def allow_syncdb(self, db, model):
       "全データベース上に全モデルを明示的に配置する"
       return True

ここで設定ファイルに、以下を追加します (path.to. は、ルータを定義した モジュールへの実際の Python パスで置き換えます):

DATABASE_ROUTERS = ['path.to.MyAppRouter', 'path.to.MasterSlaveRouter']

ルータが処理される順序は重要です。ルータは DATABASE_ROUTERS 設定に 記述されている順序で問い合わせされます。この例では MyAppRouterMasterSlaveRouter よりも前に処理されます。結果として、 myapp 中のモデル に関する決定は、他の決定が行われるより前に処理されます。もし DATABASE_ROUTERS 設定が、違う順序で 2 つのルータを並べている場合、 MasterSlaveRouter.allow_syncdb() が最初に処理されるでしょう。 MasterSlaveRouter の多目的な性質が示すのは、全モデルが全データベース上で利用可能 であろうということです。

このセットアップが導入できたら、 Django コードをいくつか走らせてみましょう:

>>> # この検索は 'credentials' データベース上で行われるでしょう
>>> fred = User.objects.get(username='fred')
>>> fred.first_name = 'Frederick'

>>> # この保存も 'credentials' に直接行われます
>>> fred.save()

>>> # これらの検索はスレーブデータベースにランダムに割り当てられます
>>> dna = Person.objects.get(name='Douglas Adams')

>>> # 新しいオブジェクトが作られた際はデータベースに対する割り当てがありません
>>> mh = Book(title='Mostly Harmless')

>>> # この割り当てはルータに助言を求め、 mh を author オブジェクトと同様に、
    # 同じデータベースに割り当てます
>>> mh.author = dna

>>> # この保存は、マスターデータベース上に 'mh' インスタンス強制します
>>> mh.save()

>>> # しかし再度オブジェクトを取り出すと、再びスレーブに戻ってきます
>>> mh = Book.objects.get(title='Mostly Harmless')
データベースの手動選択

さらに Django は、コードの中でデータベース処理を完全にコントロールできる API を 提供しています。手動でのデータベース割り当ては、ルータによるデータベース割り当て よりも優先されます。

QuerySet のためのデータベース手動選択

QuerySet “チェーン (chain)” のどの点に置いても、 QuerySet のためにデータ ベースを選択できます。ただ、 QuerySet から using() を呼び出すだけで、 特定のデータベースを使う他の QuerySet を取得できます。

using() は引数を 1 つだけ受け取ります。クエリを走らせたいデータベースへ名を 受け取ります。例えば:

>>> # これは 'default' データベースで実行されます
>>> Author.objects.all()

>>> # So will this.
>>> Author.objects.using('default').all()

>>> # これは 'other' データベースで実行されます
>>> Author.objects.using('other').all()
save() のためのデータベース選択

Model.save()using キーワードを使って、どのデータベースにデータを 保存するかを特定できます。

例えば、 legacy_users データベースへオブジェクトを保存としたいとすると、 このようにします:

>>> my_object.save(using='legacy_users')

using によって特定しない場合は、 save() はルータに割り当てられたデフォル トのデータベースに保存します。

あるデータベースから他のデータベースにオブジェクを移動する

あるデータベースにインスタンスをセーブした場合は、新しいデータベースにインスタ ンスを移行する手段として、 save(using=...) を使うためことは魅了的かもしれ ません。

以下の例を考えてください:

>>> p = Person(name='Fred')
>>> p.save(using='first')  # (命令 1)
>>> p.save(using='second') # (命令 2)

命令 1 において、新しい Person オブジェクトは first データベースに保存 されています。この場合、 p はプライマリキーを持たないので、 Django は SQL の INSERT 命令を発行します。これはプライマリキーを生成し、 Django は p に プライマリキーを付与します。

命令 2 によって保存が行われるとき、 p はすでにプライマリキーの値を保持して おり、 Djagno はそのプライマリキーを新しいデータベースで使用しようとしています。 そのプライマリキーが second データベースで使われていない場合は、何の問題は ありません。新しいデータベースにそのオブジェクトがコピーされます。

しかし、 p のプライマリキーがすでに second データベースで使われている 場合は、 second データベースの現行のオブジェクトは p が保存された際に 上書きされます。

これ避けるには 2 つの方法があります。 1 つめは、インスタンスのプライマリキーを 消してしまうことです。オブジェクトにプライマリキーが無い場合は、 Django はそれを 新しいオブジェクトとして扱います。これによって second データベースからは 何も消えません:

>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.pk = None # Clear the primary key.
>>> p.save(using='second') # Write a completely new object.

2 つめの選択肢は、 save()force_insert オプションを使うことです。 これは Django に SQL INSERT を実行させます。

>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.save(using='second', force_insert=True)

これは Fred という名の人物 (person) が、同じプライマリキーを両方のデータ ベース上で保有していることを保証します。その second データベースに書き込もう としたときにプライマリキーがすでに使われている場合、エラーが送出されます。

削除用のデータベースを選択する

デフォルトでは、既存のオブジェクトを削除するという要求は、オブジェクトの取得に 使用されたデータベース上で実行されます:

>>> u = User.objects.using('legacy_users').get(username='fred')
>>> u.delete() # will delete from the `legacy_users` database

モデルを削除するデータベースを特定するために、 using キーワード引数を Model.delete() メソッドに渡します。この引数は save()using キーワード引数と似たように機能します。

例えば、 legacy_users データベースから new_users データベースへ移行して いるとすると、以下のコマンドが使えます:

>>> user_obj.save(using='new_users')
>>> user_obj.delete(using='legacy_users')
マルチデータベースでマネジャを使う

マネジャの db_manager() メソッドを使って、マネジャアクセスを非デフォルト データベースに渡してください。

例えば User.objects.create_user() という、データベースに関するカスタムマネ ジャメソッドを持ってるとします。 create_user() はマネジャメソッドであって QuerySet メソッドではないので、 User.objects.using('new_users').create_user() とはできません (create_user() メソッドは User.objects で上のみ有効です。マネジャで あって、マネジャから得られた QuerySet オブジェクト上ではありません) 。この 解法は以下のようになります:

User.objects.db_manager('new_users').create_user(...)

db_manager() は指定したデータベースに紐づけたマネジャのコピーを返します。

マルチデータベースで get_query_set() を使う

マネジャの get_query_set() をオーバーライドする際には、 (super() を 使って) 親を呼び出すか、マネージャの _db のつく属性 (使うデータベース名 を含む文字列) を適切に扱ってください。

例えば get_query_set メソッドからカスタムされた QuerySet クラスを返し たいなら、こうしてください:

class MyManager(models.Manager):
    def get_query_set(self):
        qs = CustomQuerySet(self.model)
        if self._db is not None:
            qs = qs.using(self._db)
        return qs
Django admin インタフェースにマルチデータベースを表示する

Django の admin は、マルチデータベースの明示的なサポートはしていません。ルータ チェインによって指定された他のデータベス上のモデルのため、 admin インタフェース を提供したいなら、カスタム ModelAdmin クラスを 書く必要があります。特定のデータベースを admin インタフェースのコンテンツに 使用するように命令します。

ModelAdmin オブジェクトは 5 つのメソッドを持っています。マルチデータベースを サポートするためカスタマイズできます:

class MultiDBModelAdmin(admin.ModelAdmin):
    # A handy constant for the name of the alternate database.
    using = 'other'

    def save_model(self, request, obj, form, change):
        # Tell Django to save objects to the 'other' database.
        obj.save(using=self.using)

    def delete_model(self, request, obj):
        # Tell Django to delete objects from the 'other' database
        obj.delete(using=self.using)

    def queryset(self, request):
        # Tell Django to look for objects on the 'other' database.
        return super(MultiDBModelAdmin, self).queryset(request).using(self.using)

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        # Tell Django to populate ForeignKey widgets using a query
        # on the 'other' database.
        return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)

    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        # Tell Django to populate ManyToMany widgets using a query
        # on the 'other' database.
        return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)

この実装はここでは、マルチデータベースストラテジー機能を提供します。与えられた タイプのすべてのオブジェクトが、特定のデータベースに格納されます (例えば、 全 User オブジェクトを other データベースに)。 マルチデータベースの 使い方がより複雑なら、 ModelAdmin にそのストラテジーの反映が必要になります。

インラインは同様の方法で処理できます。メソッドを 3 つカスタマイズします:

class MultiDBTabularInline(admin.TabularInline):
    using = 'other'

    def queryset(self, request):
        # Tell Django to look for inline objects on the 'other' database.
        return super(MultiDBTabularInline, self).queryset(request).using(self.using)

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        # Tell Django to populate ForeignKey widgets using a query
        # on the 'other' database.
        return super(MultiDBTabularInline, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)

    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        # Tell Django to populate ManyToMany widgets using a query
        # on the 'other' database.
        return super(MultiDBTabularInline, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)

モデル admin 定義を書いた後は、それらは任意の Admin インスタンスに登録でき ます:

from django.contrib import admin

# Specialize the multi-db admin objects for use with specific models.
class BookInline(MultiDBTabularInline):
    model = Book

class PublisherAdmin(MultiDBModelAdmin):
    inlines = [BookInline]

admin.site.register(Author, MultiDBModelAdmin)
admin.site.register(Publisher, PublisherAdmin)

othersite = admin.Site('othersite')
othersite.register(Publisher, MultiDBModelAdmin)

この例は 2 つの admin サイトをセットアップします。 1 つめのサイトでは、 AuthorPublisher オブジェクトが見られます。 Publisher オブジェクト は、その出版社が発行した書籍を表示するインライン表を持っています。

マルチデータベースで生のカーソルを使う

2 つ以上のデータベースを使っているなら、 django.db.connections を、特定の データベースへの接続(およびカーソル)を取得するために使えます。 django.db.connections は辞書ライクオブジェクトです。これによってエイリアスを 使用して特定の接続を取得できます:

from django.db import connections
cursor = connections['my_db_alias'].cursor()
マルチデータベースの制限
クロスデータベースリレーション

Django は今のところ、複数のデータベースに及ぶ外部キーか多対多リレーションを サポートを提供していません。別のデータベースにモデルを分配するルータを使って いたなら、それらのモデルに定義される、すべての外部キーと多対多リレーションは 1 つのデータベースに入れられます。

これは参照整合のためです。2 オブジェクト間のリレーションを維持するために、 Django は正当に参照されたオブジェクトのプライマリキー知る必要があります。簡単に プライマリキーの妥当性を評価することはできません。

InnoDB で Postgres 、 Oracle 、 MySQL を使っているなら、データベースの整合性 レベルで強制されます。データベースレベルのキーの制約は、検証できないリレーション の生成を防ぎます

MyISAM テーブルで SQLite か MySQL を使っているなら、強制される参照整合性はあり ません。結果として、 ‘偽の (fake)’ クロスデータベースの外部キーができるかも しれません。しかしこの設定は Django で公式にサポートされているものではありま せん。

テーブルスペース

revision-up-to:17812 (1.4)

データベースシステムのパフォーマンスを最適化する一般的な方法は、 テーブルスペース を使ってディスクレイアウトを整理することです。

Warning

Django はテーブルスペースの作成は行いません。テーブルスペース作成、管理の 詳細については使っているデータベースエンジンのドキュメントを参照してくださ い。

テーブルのテーブルスペースを宣言する

db_tablespace オプションをモデルの class Meta に渡すことによって、モデルが生成した特定のテーブルに対してテーブルス ペースを指定することができます。 このオプションはモデルの ManyToManyField で自動的に 作られたテーブルにも影響します。

DEFAULT_TABLESPACE 設定で db_tablespace のデフォルト値を指定できます。 ビルトインの Django アプリケーションやコードを自分で制御できない他のアプリケー ションにテーブルスペースを設定するのに便利です。

インデックスのテーブルスペースを宣言する

db_tablespace オプションを Field のコンストラクタに渡すことで、その Field のカラムインデックスの 代替テーブルスペースを指定できます。そのカラムにインデックスが作られない場合は このオプションは無視されます。

DEFAULT_INDEX_TABLESPACE 設定を使って db_tablespace のデフォルト値を指定することもで きます。

もし db_tablespace が指定されず、 DEFAULT_INDEX_TABLESPACE もセットしていない場合は、インデックスは テーブルと同じテーブルスペースに作られます。

class TablespaceExample(models.Model):
    name = models.CharField(max_length=30, db_index=True, db_tablespace="indexes")
    data = models.CharField(max_length=255, db_index=True)
    edges = models.ManyToManyField(to="self", db_tablespace="indexes")

    class Meta:
        db_tablespace = "tables"

この例では、 TablespaceExample モデルによって生成されるテーブル (モデル テーブルと多対多のテーブル) は tables テーブルスペースに保存されます。 name フィールドのインデックスと多対多テーブルのインデックスは indexes テー ブルスペースに保存されます。 data フィールドもインデックスを生成しますが、 テーブルスペースが指定されていないので、デフォルトのモデルテーブルスペースであ る tables に保存されることになります。

データベースサポート

PostgreSQL と Oracle はテーブルスペースをサポートしています。 SQLite と MySQL はサポートしていません。

テーブルスペースをサポートしていないバックエンドを使う場合は、 Djang はテーブ ルスペース関連の全てのオプションを無視します。

Django 1.4 から PostgreSQL バックエンドがテーブルスペースをサポートするよ うになりました。

データベースアクセスの最適化

Django のデータベース層は、開発者がデータベースから多くを得ることを助ける色々 な方法を提供しています。このドキュメントは関連するドキュメントのリンクを集めて 色々なヒントを加え、データベース利用の最適化を試みる時に踏むべきステップを概略 する多くの見出しのもとに整理しています。

まずは計測

一般的なプログラミングの実践として、これは言うまでもないことです。 実行されるクエリと実行にかかるコスト を見てく ださい。また、 django-debug-toolbar のような外部のプロジェクトや、データベー スを直接モニタするツールを使っても良いでしょう。

必要に従って、速度を最適化するのか、メモリを最適化するのか、または両方なのかを 思い出してください。時には一方の最適化が他方にとって有害なものになりますが、ま た時には互いに助けになります。また、データベースプロセスでの処理は、 Python プ ロセスで行われる同じ量の処理と同じコストにならないかもしれません。優先度がどう か、バランスを取るべきか、全ての計測が必要か、を決定するのはあなたです。これら はアプリケーションとサーバに依存するでしょうから。

以下の全てにおいて、変更が利益を生んでいることを確かめるため、そして大きすぎる 利益がコードの可読性を下げていないかを確かめるため、変更を行うごとに計測するの を忘れないようにしてください。以下の示唆 ** 全て ** について言えることですが、 あなたの環境では一般的な原則が当てはまらないかもしれない、もしかすると逆かもし れないということに注意してください。

標準的な DB 最適化テクニックを使う

以下が含まれます:

  • インデックス。計測の結果インデックスを追加するべきだと決心した 後には これ を最も優先すべきです。インデックスを追加するには django.db.models.Field.db_index を使います。
  • 適切なフィールドの型を使います。

上記の明らかなことは既に行ってあると仮定します。このドキュメントの残りでは、必 要のないことをしないためにどのように Django を使うかに焦点を当てます。このド キュメントはまた、 汎用的なキャッシング のような高くつ く操作全般に適用するような最適化技術は扱いません。

クエリセットを理解する

クエリセット を理解することはシンプルなコードで 良いパフォーマンスを得るために欠かせません。とりわけ:

クエリセットの評価を理解する

パフォーマンスの問題を避けるには、次のことを理解することが大切です:

キャッシュされる属性を理解する

QuerySet 全体をキャッシュするのと同様、 ORM オブジェクトの属性の結果も キャッシュされます。一般的に呼び出し可能でない属性はキャッシュされます。例えば ブログでのモデル例 を前提にすると:

>>> entry = Entry.objects.get(id=1)
>>> entry.blog   # Blog オブジェクトはこの時点で検索されます
>>> entry.blog   # キャッシュバージョンが使われ、 DB アクセスしません

しかし一般的に、呼び出し可能な属性は毎回 DB 参照を行います:

>>> entry = Entry.objects.get(id=1)
>>> entry.authors.all()   # クエリが実行された
>>> entry.authors.all()   # クエリがまた実行された

テンプレートコードを読む時には注意してください。テンプレートシステムはカッコを 使うことを許容しませんが、上記の区別なく呼び出し可能オブジェクトを自動的に呼び ます。

自分のカスタムプロパティに注意してください。キャッシュを実装するかは開発者に任 されています。

with テンプレートタグを使う

QuerySet のキャッシング動作を利用するには、 with テンプレートタグ を使う必要があるかもしれません。

iterator() を使う

多くのオブジェクトを持っている時、 QuerySet のキャッシング動作は大量のメモ リ使用につながる可能性があります。この場合、 iterator() が助けになるかもしれませ ん。

Python よりもデータベースで行うべき動作

具体例として:

  • 最も基本的なレベルでは、 filter と exclude を使って データベースのフィルタリングをすることができます。

必要なSQLを生成するのにこれらで不十分なら:

QuerySet.extra() を使う

移植性は比較的低いですが、より強力なメソッドが extra() です。これはクエリに対する明 示的な SQL の追加を可能にします。これでもまだ十分強力じゃないなら:

生の SQL を使う

データを検索したりモデルを作成するためのカスタム SQL を自分で書いてください。 django.db.connection.queries を使うと、 Django が 書き出したクエリを見ることができますので、そこから始めましょう。

必要になると分かっているデータを一度に検索する

一般的に、一つのデータ「集合」の別々の部分のために、データベースを複数回叩くの は、一つのクエリで一度に検索するよりも効率が低いです。 もしクエリがループの中で実行されていて、それ故に多くのデータベースクエリを一つ だけにまとめることが必要なら、特に重要です。

必要ないものを検索しない
QuerySet.values()values_list() を使う

値の dictlist が必要なだけで、 ORM モデルオブジェクトが必要ないな ら、 values() を適切に使ってください。 これらはテンプレートコードのモデルオブジェクトを置き換えるのに便利です。辞書が テンプレートで使われているものと同じ属性を持っているなら、うまく行きます。

QuerySet.defer()only() を使う

必要のない (またはほとんどの場合に必要ない) データベースカラムがあると分かって いるなら、それらをロードしないように defer()only() を使ってください。 もしそのようなカラムを 使う 場合は、 ORM が個別のクエリで取りに行かなければ ならないことに注意してください。不適切にそれを使うなら、悲観的に考えましょう。

また、遅延フィールドを使ってモデルを構築する時に Django の中でいくらかの (小さ な追加の) オーバーヘッドが発生することに注意してください。計測なしでの遅延 フィールドの使用に積極的になりすぎないでください。わずかなカラムしか使っていな い時でも、データベースは結果の 1 つの行のためにほとんどの非 text 型、非 VARCHAR 型のデータをディスクから読み出さなければなりません。 defer()only() メソッドは多くのテキストデータや Python データに戻すために多くの処 理が必要となるフィールドのロードを避けるのにはとても便利です。例によって、まず は計測、それから最適化をしましょう。

QuerySet.count() を使う

数を数えたいだけなら、 len(queryset) を使うよりもこちらの方が良いです。

QuerySet.exists() を使う

少なくとも 1 つの結果が存在するかを調べたいだけなら、 if queryset よりもこ ちらの方が良いです。

しかし:

count()exists() を使いすぎない

QuerySet からの他のデータが必要なら、それを評価しましょう。

例えば、 body 属性とユーザへの多対多のリレーションを持っている電子メールモ デルを考えましょう。以下のテンプレートコードは最適です:

{% if display_inbox %}
  {% with emails=user.emails.all %}
    {% if emails %}
      <p>You have {{ emails|length }} email(s)</p>
      {% for email in emails %}
        <p>{{ email.body }}</p>
      {% endfor %}
    {% else %}
      <p>No messages today.</p>
    {% endif %}
  {% endwith %}
{% endif %}

これが最適な理由は:

  1. QuerySets が遅延評価されるので display_inbox が False の場合にデータベース アクセスが発生しません。
  1. with を使うと、あとで使われるために user.email_all を変数に保存 します。再利用できるキャッシュが使われます。
  1. {% if emails %} 行は QuerySet.__nonzero__() の呼び出しを引き起こし ます。これは user.emails.all() がデータベースで実行されることにつながり ます。少なくとも一行目が ORM オブジェクトに変換されます。結果がなければ False を返し、あれば True を返します。
  1. {{ emails|length }}QuerySet.__len__() を呼びます。他のクエリを 実行せずにキャッシュの残りを満たします。
  1. for ループは既に満たされているキャッシュの上に繰り返されます。

全体として、このコードは 1 個か 0 個のデータベースクエリを実行します。 唯一の計画的な最適化の実施が with タグ の使用です。どの箇所で QuerySet.exists()QuerySet.count() を使っても追加のクエリの原因にな ります。

QuerySet.update()delete() を使う

オブジェクトを検索してロードし、何かの値をセットして個別に保存するよりも、 QuerySet.update() 経由でバルクの SQL UPDATE 文を使うほうが良いです。同 様に、可能な限り バルクでの削除 を使いましょ う。

しかしながら、これらのバルクアップデートメソッドは個別のインスタンスの save()delete() メソッドを呼べないことに注意してください。つまり、 通常のデータベースオブジェクト シグナル によって起動され るものも含め、開発者がこれらのメソッドに追加したいかなるカスタムの動作も実行さ れないことになります。

外部キーの値を直接使う

外部キーの値が必要なだけなら、関連するオブジェクトの全体を取得してそのプライマ リキーを得るのではなく、既に取得しているオブジェクトの外部キーの値を使いましょ う。例えばこのようにする代わりに:

entry.blog.id

こうします:

entry.blog_id
バルクでのインサート

複数のオブジェクトを作る時、可能であれば、 bulk_create() メソッドを使って SQL ク エリの数を減らしましょう。例えば:

Entry.objects.bulk_create([
    Entry(headline="Python 3.0 Released"),
    Entry(headline="Python 3.1 Planned")
])

が以下よりも望ましいです:

Entry.objects.create(headline="Python 3.0 Released")
Entry.objects.create(headline="Python 3.1 Planned")

このメソッドに関する注意 がたくさんありますので、ユースケースに合致しているか確認してください。

このことは ManyToManyFields にも当 てはまります。このようにすることは:

my_band.members.add(me, my_friend)

以下よりも良いです:

my_band.members.add(me)
my_band.members.add(my_friend)

ここで BandsArtists は多対多の関係を持っているとします。

HTTP リクエストの処理

revision-up-to:11321 (1.1)

このセクションでは、 Django における HTTP リクエスト処理に関する情報を扱っ ています:

URL ディスパッチャ

revision-up-to:17812 (1.4)

すっきりした、簡潔で明解な URL スキームの設計は、高品質の Web アプリケーショ ンでは重要な要素です。 Django では、フレームワークの制限なしに、望み通りの URL を設計できます。

.php.cgi といった拡張子は必要なく、 0,2097,1-1-1928,00 のよ うな意味不明の URL にする必要もありません。

URL はすっきりした扱いやすいものにすべきであるという主張については、ワール ドワイドウェブの産みの親である Tim Berners-Lee の優れた解説、 Cool URIs don’t change を参照してください。

概要

あるアプリケーション向けの URL を設計するには、 URLconf (URL 設定: URL configuration) と呼びならわされている Python モジュールを作成します。このモ ジュールは pure Python のコードで、中にはただ URL パターン (単純な正規表現) から Python のコールバック関数 (自作のビュー) へのマッピングが入っているだ けです。

Django はまた、 URL が稼働している言語から翻訳することを許可しています。 このプロセスは、 internationalization docs で、説明 しています。

このマッピングは短くもできますし、必要なだけ長くもできます。他のマッピング も参照できます。また、 pure Python コードなので、動的に生成できます。

Django のリクエスト処理

ユーザが Django で作られたサイト上のページをリクエストした時に、どの Python コードが実行されるかは以下のアルゴリズムで決定されます:

  1. まず、Django は、どのモジュールをルート URLconf として使うか決定しま す。もともとは、この値は ROOT_URLCONF に設定されています。ただし、 HttpRequest オブジェクトに urlconf という属性が設定されてい た場合( request processing で設定されます) 、その値を ROOT_URLCONF の代わりに使います。
  2. Django は上記の Python モジュールをロードして、 urlpatterns とい う名前の変数を探します。この変数の値は Python のリストで、 django.conf.urls.patterns() が返すのと同じ形式です。
  3. Django は URL パターンを順に調べて、リクエスト URL に最初にマッチし たところで止まります。
  4. 何らかの正規表現にマッチしたら、 Django はマッピングされているビュー を import して呼び出します。ビューは単純な Python 関数です。ビュー関 数の第一引数には HttpRequest が、それ以降の引 数には正規表現でキャプチャした値が渡されます。
  5. もし、正規表現が何にもマッチしなかったり、パターンマッチングプロセスの 途中のどこかで例外が発生した場合、 Django は、適切なエラーハンドリング ビューを呼び出します。 エラーハンドリング を見てください。
凡例

URLconf の一例を示します:

from django.conf.urls import patterns, url, include

urlpatterns = patterns('',
    (r'^articles/2003/$', 'news.views.special_case_2003'),
    (r'^articles/(\d{4})/$', 'news.views.year_archive'),
    (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
    (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)

注意:

  • URL 中の値を取り出すには、単に丸括弧で囲むだけです。
  • 先頭のスラッシュは必要ありません。全ての URL に共通だからです。例えば、 ^/articles ではなく ^articles にします。
  • 正規表現文字列のリテラルの前に 'r' があります。これは必須ではあり ませんが、付けた方がよいでしょう。 r は、”raw” 文字列、すなわち文 字列中のいかなる文字もエスケープしないことを表します。 Dive Into Python’s explanation も参考にしてください。

例のリクエストを挙げて説明しましょう:

  • /articles/2005/03/ へのリクエストは 3 つめのエントリにマッチしま す。 Django は news.views.month_archive(request, '2005', '03') を 呼び出します。
  • /articles/2005/3/ はどの UrL パターンにもマッチしません。リストの 3 つめのエントリでは月番号が 2 桁になるよう要求しているからです。
  • /articles/2003/ はリストの 2 番目ではなく、最初のパターンにマッチ します。パターンは順番に調べられ、最初にテストにパスしたものが使われ るからです。この法則を、特別扱いしたい URL の処理に使ってもかまいませ ん。
  • /articles/2003 は URL がスラッシュで終わっていないので、どのパター ンにもマッチしません。
  • /articles/2003/03/3/ は最後のパターンにマッチします。 Django は news.views.article_detail(request, '2003', '03', '3') を呼び出し ます。
名前つきグループ

上の例では、 名前なしの 簡単な正規表現グループを (丸括弧表記で) 使って URL から情報を取り出し、ビュー関数に 固定引数 として渡していました。より 高度な方法に、 名前つきの 正規表現グループで URL から情報を取り出し、ビュー の キーワード引数 に渡すものがあります。

Python の正規表現では、名前つき正規表現グループは、 name をグループの名 前、 pattern をマッチさせるパターンとして、 (?P<name>pattern) で表 されます。

上の URLconf の例を書き換えて、名前つきグループにしたものを示します:

urlpatterns = patterns('',
    (r'^articles/2003/$', 'news.views.special_case_2003'),
    (r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
    (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
    (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'news.views.article_detail'),
)

この URLconf は前の例と全く同じ機能を実現しますが、一つだけはっきり違うのは、 取り出した値を固定引数ではなくキーワード引数としてビュー関数に渡すという点 です。例えば:

  • /articles/2005/03/ へのリクエストは、 news.views.month_archive(request, year='2005', month='03') ではなく news.views.month_archive(request, '2005', '03') を呼び出します。
  • /articles/2003/03/3/ へのリクエストは news.views.article_detail(request, year='2003', month='03', day='3') を呼び出します。

実際には、このような名前つきグループを使うと URLconf が少しだけ明示的な書き 方になり、引数の順番に起因するバグを起こしにくくなります – それに、自作ビュー の関数定義で引数順を変更できます。いうまでもなく、こうした恩恵は簡潔さの犠 牲の上に成り立っています: 開発者の中には、名前つきグループの構文が醜くて冗長 だと感じる人もいます。

マッチ/グルーピングのメカニズム

URLconf パーザが正規表現中の名前つきグループと名前なしグループを扱う際のア ルゴリズムを以下に示します:

名前つきの引数があればそれを使います。名前なし引数は無視します。名前つきの 引数がなければ、すべての名前なし引数を固定引数として使います。

どちらの場合でも、追加のキーワード引数を指定できます。後述の「ビュー関数に 追加のオプションを渡す」を参照してください。

URLconf の検索対象

URLconf はリクエストされた URL を通常の Python 文字列として検索します。この 文字列には GET や POST のパラメタも、ドメイン名も入りません。

例えば、 http://www.example.com/myapp/ URLconf が検索対象にするのは myapp/ です。

http://www.example.com/myapp/?page=3 へのリクエストの場合でも myapp/ です。

URLconf はリクエストメソッドを調べません。換言すれば、同じ URL に対する POST, GET, HEAD といった全てのリクエストメソッドは同じビュー関 数に送られます。

urlpatterns の書き方

urlpatternsdjango.conf.urls.patterns`() の返す形式の Python リストになります。 urlpatterns 変数の値を作るときには、常に patterns() を使うようにして下さい。

django.conf.urls 汎用的な関数

Deprecated since version 1.4: Django 1.4 の関数 patterns, url, include に加えて handler*django.conf.urls モジュールの中にあり、以下で説明さ れているシンボル群です。

Django 1.3 までは、これらは django.conf.urls.defaults の中に位置していました。まだ、以前のモジュールからインポート することが出来ますが、 Django 1.6 でそれが出来なくなります。

patterns
patterns(prefix, pattern_description, ...)

プレフィクス (prefix) 引数、および任意の個数の URL パターンをとり、Django で必要な形式の URL パターンのリストを返す関数です。

patterns() の最初の引数は プレフィクス (prefix) 文字列です。後述の 「 ビュープレフィクス 」 を参照してください。

残りの引数は以下の形式のタプルにします:

(正規表現, Pythonコールバック関数 [, パラメタ辞書 [, エントリ名]])

パラメタ辞書 および エントリ名 はオプションで、省略できます (詳しくは「 ビュー関数に追加のオプションを渡す 」を参照してください)。

Note

patterns() は関数呼びだしの形式を取っているので、取れる引数 (URL パ ターン) は最大 255 個に制限されています。これは全ての Python 関数呼びだ しに共通する制約です。通常は、 URL パターンを include() でモジュラ に構造化するものなので、事実上この制約はほとんど問題になりません。とは いえ、運悪く 255 個の制約に引っかかってしまうようなら、 pattern() が Python のリストを返すということを思い出して、以下のように引数を分割 し、リストを構築するとよいでしょう:

urlpatterns = patterns('',
    ...
    )
urlpatterns += patterns('',
    ...
    )

Python のリスト型にはサイズ制限がありませんから、いくつでも URL パター ンを定義できます。ただし、一度に 254 個づつ (先頭のプレフィクス引数のた めに、 255 個のうちの一つを使います) しか定義できないという制約に注意し てください。

url
url(regex, view, kwargs=None, name=None, prefix='')

url() 関数は、タプルの代わりとして patterns() の引数に指定できます。 この関数は、パラメタ辞書がないときに URLconf のエントリ名を指定したい場合に 便利です。例えば、以下のように用います:

urlpatterns = patterns('',
    url(r'/index/$', index_view, name="main-view"),
    ...
)

この関数は 5 つの引数をとり、そのほとんどが省略可能です:

url(regex, view, kwargs=None, name=None, prefix='')

name パラメタが便利な理由は「 URL パターンに名前を付ける 」の節を参 照してください。

prefix パラメタは patterns() の最初の引数と同じ意味を持ち、 view を文字列形式で指定する場合にしか使われません。

include
include(<module or pattern_list>)

関数は現在の URLconf で “included” しておくべき Python のフル import パス を他の URLconf モジュールへと 取りこみます。

include() も引数としてイテレート可能です。これは、 URL パターンを返します。

他の URLconf を取り込む を見てください。

エラーハンドリング

Django が、リクエストされた URL にマッチする正規表現を 見つけられなかったとき、または例外が発生したとき、 Django は error-handling view を発生させます。ビューはルートの URlconf に設定された三つの変数で、指定されたケースで使用 されます。これらの変数の設定は、他のどの URLconf にも影響を 与えません。

エラービューのカスタマイズ を参照してください、もっと詳しいことが載っています。

handler403
handler403

呼ぶことができます、またはユーザがリソースにアクセスするのに 必要なパーミッション(permission 権限)を持っていない場合に、 ビュー関数への完全な Python の import パスが入った文字列です。

通常、これは 'django.views.defaults.permission_denied' これのデフォルト値は満足させるでしょう。

403 (閲覧が禁止されています ビュー) にもっと沢山の情報が載っています。

handler403 は Django 1.4の新しいものです
handler404
handler404

呼び出せるか、マッチする URL パターンがない場合に呼び出されるビュー関数への、 完全な Python の import パスが入った文字列です。

デフォルト値は、 'django.views.defaults.page_not_found' です。 普通はこのデフォルト値で十分のはずです。

前のバージョンの Django では、 import パスが入った文字列のみを、受け取れ ます。
handler500
handler500

呼び出しか、サーバエラーが生じた場合に呼び出されるビュー関数への、 完全な Python の import パスが入った文字列です。 サーバエラーはビュー関数のコード中で実行時エラーが生じたときに起きます。

デフォルト値は、 'django.views.defaults.page_not_found' です。 普通はこのデフォルト値で十分のはずです。

前のバージョンの Django では、 import パスが入った文字列のみを、受け取れ ます。
URL 中のテキストを取り出す際の注意

引数として取り出したテキストは、正規表現が何にマッチするかに関係なく通常の Python 文字列型になります。例えば、下のような URLconf の行:

(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),

では、 news.views.year_archive() に渡される year 引数は、整数ではな くあくまでも文字列になります。 \d{4} が整数を表す文字列にしかマッチしな いことは関係ありません。

よく使うトリックとして、ビュー関数の引数にデフォルトパラメタを指定しておく という方法があります。 URLconf とビューの一例をあげて説明しましょう:

# URLconf
urlpatterns = patterns('',
    (r'^blog/$', 'blog.views.page'),
    (r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
)

# View (in blog/views.py)
def page(request, num="1"):
    # Output the appropriate page of blog entries, according to num.

上の例では、 URL パターンはどちらも同じビュー、 blog.views.page を指し ています。しかし、最初のパターンでは、 URL から何の情報も取り出しません。最 初のパターンがマッチすると、 page() 関数は num の値として、デフォル トの "1" を使います。二つめのパターンがマッチすると、 page() は正規 表現で取り出した num の値を使います。

パフォーマンス

urlpatterns 内の各正規表現は、最初にアクセスした際にコンパイルされます。 これによって、システムは劇的に高速になります。

ビュープレフィクス

コードを繰り返し書かなくてもよいように、 patterns() 呼び出しの中で共通 のプレフィクスを指定できます。

Django の概要 からとってきた URLconf の例を示します:

from django.conf.urls import patterns, url, include

urlpatterns = patterns('',
    (r'^articles/(\d{4})/$', 'news.views.year_archive'),
    (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
    (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)

この例では、各ビューには共通のプレフィクス、 'news.views' があり ます。 urlpatterns の各エントリに繰り返し入力する代わりに、 patterns() 関数の最初の引数にプレフィクスを指定して、各ビュー関数に適用 できます。

このことを念頭に置いて、上記の例をもっと簡潔に書くと以下のようになります:

from django.conf.urls import patterns, url, include

urlpatterns = patterns('news.views',
    (r'^articles/(\d{4})/$', 'year_archive'),
    (r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
    (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
)

プレフィクスには末尾のドット (".") を入れていないことに注意してください。 Django はドットを自動的に入れます。

複数のビュープレフィクス

現実的には、色々なビューを取り混ぜて使うことになり、 urlpatterns に共通 のプレフィクスを持たせられなくなることでしょう。しかし、パターンの重複を防 ぐビュープレフィクスのショートカット機能には、もう一つの利用法があります。 それは、以下のように複数の patterns() オブジェクトを足し合わせるという ものです:

古い書き方:

from django.conf.urls import patterns, url, include

urlpatterns = patterns('',
    (r'^$', 'django.views.generic.date_based.archive_index'),
    (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'django.views.generic.date_based.archive_month'),
    (r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'),
)

は、以下のような書き方に改められます:

from django.conf.urls import patterns, url, include

urlpatterns = patterns('django.views.generic.date_based',
    (r'^$', 'archive_index'),
    (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','archive_month'),
)

urlpatterns += patterns('weblog.views',
    (r'^tag/(?P<tag>\w+)/$', 'tag'),
)
他の URLconf を取り込む

urlpatterns 好きな場所で、他の URLconf モジュール上の定義を取り込めます。 この操作は、本質的には一連の URL を他の URLconf の下に「すげかえる(root)」 操作になります。

例として、 Django ウェブサイト 自体の URLconf の例外を示します。この URLconf は他の URLconf をいくつも取り込んでいます:

from django.conf.urls import patterns, url, include

urlpatterns = patterns('',
    # ... snip ...
    (r'^comments/', include('django.contrib.comments.urls')),
    (r'^community/', include('django_website.aggregator.urls')),
    (r'^contact/', include('django_website.contact.urls')),
    (r'^r/', include('django.conf.urls.shortcut')),
    # ... snip ...
)

この例の正規表現が、 $ (行末にマッチする文字) を使っておらず、末尾にスラッ シュを付けていることに注意しましょう。 Django が include() をみつけると、 URL からマッチした部分を取り去り、残りの文字列を include された URLconf に 送って続きの処理を行わせます。

URLconf を Python モジュールで include 引数で指定する方法ではなく、 追加した URL patterns を含める別の可能性として、 patterns によって 返されたパターンリストを直接的に使う方法があります。:

from django.conf.urls import patterns, url, include

extra_patterns = patterns('',
    url(r'^reports/(?P<id>\d+)/$', 'credit.views.report', name='credit-reports'),
    url(r'^charge/$', 'credit.views.charge', name='credit-charge'),
)

urlpatterns = patterns('',
    url(r'^$',    'apps.main.views.homepage', name='site-homepage'),
    (r'^help/',   include('apps.help.urls')),
    (r'^credit/', include(extra_patterns)),
)

この手法は、 Django のアドミンアプリケーションのインスタンスを配置する時に 見られます。 Django のアドミンは、 AdminSite のインスタンスとして 配置されます。それぞれの ~django.contrib.admin.AdminSite インスタンスは urls アトリビュートを持っていて、 それらは url パターンとしてインスタンスに使用可能です。 それは、アドミンのインターフェイスを配置する時に、 include() を プロジェクトの urlpatterns に入れることができます。

キャプチャされたパラメタ

include された側の URLconf は、親となる URLconf で取り出された全てのパラメ タを受け継ぎます。従って、以下のような例はうまく動作します:

# In settings/urls/main.py
urlpatterns = patterns('',
    (r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)

# In foo/urls/blog.py
urlpatterns = patterns('foo.views',
    (r'^$', 'blog.index'),
    (r'^archive/$', 'blog.archive'),
)

上の例では、 include された側にも "username" 変数が期待どおりに渡されま す。

URL の名前空間を定義する

もし、複数のインスタンスを一つのアプリケーションで必要としたならば、 インスタンスの間で区別を付けられることが役立つでしょう。 これは、一つのアプリケーションが分割する URL の名前付けの 複数のインスタンスにおいて URL の名前付け を使う時に 特に大切になります。一つのアプリケーションの中の複数の インスタンスは、名前づけられた URL を分割します、名前空間は インスタンスそれぞれに名前づけられた URL の分割を教える方法です。

URL の名前空間は二つのパートになります、両方とも文字列です:

  • 一つは application namespace です。これはデプロイ(deploy)されている アプリケーションの名前を示します。単一のアプリケーションの全てのインスタンス は、同じアプリケーションの名前空間 を持ちます。例えば、 Django のアドミンアプリケーションは幾分予測可能な アプリケーションの名前空間を持っています admin です。
  • そして、 instance namespace です。この識別子はアプリケーションの特有の インスタンスです。インスタンスの名前空間は、プロジェクト全体に渡り、 一意のものです。これはアプリケーションの通常のインスタンスを指定するために 使われます。例えば、通常 Django のアドミンインスタンスは admin の インスタンスの名前空間を持っています。

URL の名前空間は二つの方法によって指定されています。

始めに、URL パターンを構成する時、アプリケーションとインスタンスの名前空間を、 include() の引数として、渡すことができます。:

(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar'))

これは apps.help.urls のなかで定義された URL を含み、インスタンスの 名前空間 bar と、アプリケーションの名前空間 foo へといきます。

二つ目は、埋め込まれた名前空間のデータを内包するオブジェクトを含むことができま す。もし、 patterns オブジェクトを include() したなら、オブジェクトは グローバルな名前空間へと加えられます。しかしながら、三つのタプルを 含んだオブジェクトを include() することもできます:

(<patterns object>, <application namespace>, <instance namespace>)

これは、与えられたアプリケーションとインスタンスの名前空間へと指定された URL パターンを含みます。例えば、 Django の AdminSite オブジェクトの urls アトリビュートは 3つのタプルを返し、それはアドミンサイトの 全てのパターンを含んでいます。そして、アドミンインスタンスの名前を足すと アプリケーション名前空間の admin です。

一度定義した、名前空間化された URL は、名前空間から逆引きしてたどること が出来ます。もっと詳しく、名前空間づけた URL を逆引きする方法について知るには、 URL 名前空間 を見てください。

ビュー関数に追加のオプションを渡す

URLconf には、ビュー関数に追加の引数を Python の辞書型で渡せるようにするた めのフックがあります。

URLconf タプルにはオプションとして 3 つめの要素を指定できます。この要素は ビューに渡したい追加の引数からなる辞書にします。

例えば:

urlpatterns = patterns('blog.views',
    (r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
)

この例では、 /blog/2005/ というリクエストに対し、 Django は blog.views.year_archive() ビューを呼び出し、以下のキーワード引数を渡し ます:

year='2005', foo='bar'

このテクニックは 汎用ビュー配信フィードフレームワーク で、メタデータ やビューへのオプションを渡すために使われています。

衝突を避ける

URLconf を書くとき、名前付きのキーワード引数をキャプチャさせ、同時に追 加引数の辞書に同名の項目を追加するような書き方ができてしまいます。この 場合、辞書に設定した内容が優先されます。

include() に追加のオプションを渡す

include() にも追加のオプションを渡せます。 include() に追加のオプショ ンを渡すと、そのオプションは include された側の URLconf の 全ての URL 設定 に適用されます。

例えば、以下の二つの URLconf は機能的に全く同じです:

その 1:

# main.py: include する側
urlpatterns = patterns('',
    (r'^blog/', include('inner'), {'blogid': 3}),
)

# inner.py: include される側
urlpatterns = patterns('',
    (r'^archive/$', 'mysite.views.archive'),
    (r'^about/$', 'mysite.views.about'),
)

その 2:

# main.py: include する側
urlpatterns = patterns('',
    (r'^blog/', include('inner')),
)

# inner.py: include される側
urlpatterns = patterns('',
    (r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
    (r'^about/$', 'mysite.views.about', {'blogid': 3}),
)

追加オプションは、ビューがオプションを実際に有効なオプションとして受け取る かどうかに関わらず、 常に include される側の URLconf の 全ての 行に渡さ れるので注意してください。このため、 include に追加オプションを渡すテクニッ クが有効なのは、include される側の URLconf に入っている全てのビューが、追加 のオプションを受け取れるとはっきりしている場合だけです。

文字列の代わりに呼び出し可能オブジェクトを渡す

開発者の中には、ビューのパスが入った文字列の代わりに、ビューそのものを表す Python 関数オブジェクトを渡す方が自然だと考えている者もいます。この代替案も サポートされており、任意の呼び出し可能オブジェクトをビューに渡せます。

例えば、 以下のような「文字列の」URLconf があったとします:

urlpatterns = patterns('',
    (r'^archive/$', 'mysite.views.archive'),
    (r'^about/$', 'mysite.views.about'),
    (r'^contact/$', 'mysite.views.contact'),
)

文字列の代わりにオブジェクトを渡して、同じ機能の URLconf を実現できます。オ ブジェクトを忘れず import してください:

from mysite.views import archive, about, contact

urlpatterns = patterns('',
    (r'^archive/$', archive),
    (r'^about/$', about),
    (r'^contact/$', contact),
)

以下の例も機能的に全く同じです。個々のビューを個別に import する代わりにビュー の入ったモジュールを import すると、すこしだけすっきり書けます:

from mysite import views

urlpatterns = patterns('',
    (r'^archive/$', views.archive),
    (r'^about/$', views.about),
    (r'^contact/$', views.contact),
)

どのスタイルを使うかは自由です。

このテクニックを使った場合 (文字列の代わりにオブジェクトを渡した場合) 上で説明した “”ビュープレフィクス”” は効果を及ぼさないので注意してください。

URL パターンに名前を付ける

URLconf 中の複数の URL パターンから同じビュー関数を呼ぶケースはよくあります。 たとえば、以下の二つの URL パターンは、どちらも archive というビューを 指しています:

urlpatterns = patterns('',
    (r'/archive/(\d{4})/$', archive),
    (r'/archive-summary/(\d{4})/$', archive, {'summary': True}),
)

この URLconf には何ら問題はないのですが、 (permalink() デコレータや {% url %} を使って) 逆引き URL マッチング (reverse URL matching) を 行うと問題を引き起こします。というのも、二つの URLパターンが同じビューを指 しているために、Django の逆引き URL マッチング機構は、 archive ビューに 対する正しい URL がわからなくなってしまうからです。

この問題を解決するために、Django は 名前付き URL パターン をサポートし ています。すなわち、各 URL パターンに名前をつけて、同じビューやパラメタを使っ ているパターンどうしを区別できます。その上で、付けた名前を使って逆引き URL マッチングを行えます。

例えば、 URLconf が以下のようになっていたとしましょう:

urlpatterns = patterns('',
    url(r'/archive/(\d{4})/$', archive, name="full-archive"),
    url(r'/archive-summary/(\d{4})/$', archive, {'summary': True}, "arch-summary"),
)

上記に記した名前(full-archive and arch-summary)を使って、個別にそれ ぞれのパターンの URL にアクセスすることができます。

{% url arch-summary 1945 %}
{% url full-archive 2007 %}

つまり、二つの URL パターンがどちらも同じく archive ビューを参照してい ても、 url()name パラメタを指定すれば、テンプレート中で二つを区 別できるというわけです。

URLconf エントリに付ける名前は、どんな文字を入れても構いません。 Python の 変数名規則には縛られません。

Note

URLconf エントリに名前を付ける場合、他のアプリケーションの名前と衝突し なさそうな名前を使ってください。例えば、ある URL パターンを “comment” と命名していて、他のアプリケーションでも同じ名前を使っていたとしましょ う。この名前をテンプレートで使うと、もはやどちらの URL がテンプレートに 挿入されるのか判断する術はありません。

URLconf エントリの名前に、例えばアプリケーション名などからとったプレフィ クスを追加しておけば、名前が衝突しにくくなってよいでしょう。というわけ で、単に “comment” ではなく、 “myapp-comment” にするよう勧めます。

URL 名前空間

名前空間化された URL は : 演算子を使うことによって指定されます。 例えば、アドミンアプリケーションの、メインのインデックスページは admin:index を使うことによって参照されます。

名前空間は、ネスト(nest 入れ子)することができます。 foo:bar:whiz と名前づけられた URL は、トップレベルの名前空間である foo の中から bar という名前で定義されたものを探し、さらにその中から whiz という名前のパターンを探します。

名前空間化された URL (すなわち、myapp:index のようなもの) が与えられる て解決すると、 Django は完全にパス化されたものをパーツごとに分解しようとしま す。そしてその時に、 URL の探索を行おうとします。

  1. 最初に、 Django はアプリケーションの名前空間とマッチするものを探そうとし ます。(この例では myapp です。)これは、アプリケーションのインスタンスの リストを作ります。

  2. もし、 現在の アプリケーションが定義されていれば、 Django は インスタンスの為に URL リゾルバを見つけて返します。 現在の アプリケーション は、テンプレートコンテキストの上のアトリビュートとして指定されます。 アプリケーションは複数の配置を持つことを想定していて、テンプレートのレンダ リングをするために使われる ContextRequestContextcurrent_app アトリビュートに指定されます。

    現在のアプリケーションは reverse() 関数の引数から、手動で 指定することも出来ます。

  3. もし、現在のアプリケーションがないとしたらどうでしょう。 Django は デフォルトのアプリケーションインスタンスを探します。デフォルトの アプリケーションインスタンスは、アプリケーションの名前空間とマッチする インスタンスの名前空間を持つインスタンスです。(この例では、 myapp のインスタンスは myapp と呼ばれます)

  4. もし、デフォルトのアプリケーションインスタンスがなかったとしたら、 Django は一番初めに配置されたアプリケーションのインスタンスを選択します。 それは、インスタンスの名前がどんなものであってもそうでしょう。

  5. もし、提供された名前空間がステップの1のアプリケーションの名前空間と マッチしない場合、 Djangoはインスタンスの名前空間として、直接参照しようと します。

もし入れ子になった名前空間なら、ビューの名前が逆引きできるまで、名前空間の それぞれのパートに対してこれらのステップを繰り返します。

この行動の解決戦略をお見せしましょう、 myapp: の foo と呼ばれる インスタンスと、 bar と呼ばれるものについて考えてみましょう。 myapp はメインのインデックスページを持っていて、 URL の名前は index で す。セットアップを使って、参照可能にしてみましょう。:

  • もし、インスタンスのうち一つが現在のものであれば、言い換えると、もし bar インスタンスの中でユーティリティページをレンダリングしならば、 myapp:index は、 bar インスタンスのインデックスページとして 逆引きします。
  • もし、現在のインスタンスがなければ、言い換えると、もしサイトのどこかで ページのレンダリングを行うならば、 myapp:index は最後に登録された myapp のインスタンスを逆引きします。これらは通常のインスタンスではなく、 myapp の最後のインスタンスが使われます。これは foobar で、プロジェクトの URL パターンに導入された順番に依存します。
  • foo:index は常に foo インスタンスのインデックスページを解決しようと します。

もし、通常のインスタンスも存在すれば、すなわち、 myapp と名前づけられた インスタンスが存在すると、このようなことがおきます。:

  • もし、インスタンスのうち一つが現在のものであれば、言い換えると、もし bar インスタンスの中でユーティリティページをレンダリングしならば、 myapp:index は、 bar インスタンスのインデックスページとして 逆引きします。
  • もし、現在インスタンスがなければ、言い換えるとサイトのどこかのページを レンダリングするなら myapp:index は、通常のインスタンスの インデックスページとして逆引きします。
  • foo:index は、再び foo インスタンスのインデックスページへと 逆引きします。
django.core.urlresolvers ユーティリティ関数
reverse()

コード中で url のようなことをしたい場合のために、Django では django.core.urlresolvers.reverse モジュールを提供しています:

reverse(viewname[, urlconf=None, args=None, kwargs=None, current_app=None])

viewname は関数名 (関数への参照か、関数名の文字列表記で、 urlpatterns 上で使っている形式) または URL パターン名 です。通常、 urlconf パラメタについて考える必要はなく、以下のように、固定引数とキー ワード引数だけを指定して url マッチングを行います:

from django.core.urlresolvers import reverse

def myview(request):
    return HttpResponseRedirect(reverse('arch-summary', args=[1945]))

reverse() 関数は大抵の正規表現パターンを URL に逆変換できますが、全てと は限りません。今のところ、パターン中に垂直バー ("|") を含められません。 垂直バーを含むパターンは、 URL からビューへのマッチングには問題なく使えます が、パターンの逆変換はできないので注意してください。

current_app 引数は、リゾルバが現在実行しているるビューが属している アプリケーションを指し示すためのヒントを提供します。この current_app 引数は、アプリケーションの名前空間をひも解き、指定したアプリケーション インスタンスへと導くヒントとして扱われ、 URL 名前空間 に従っています。

args の代わりに、 kwargs を用いることもできます。例えば:

>>> reverse('admin:app_list', kwargs={'app_label': 'auth'})
'/admin/auth/'

argskwargs は、 reverse() を同時に通過することはできません。

ビューが全て正しいことを確認してください

URL 名の種々のパターンへのひも付けの生成の一部として、 reverse() 関数は、 URLconf ファイルのすべてと調べる各ビューの名前 をインポートする必要があります。これは各ビュー関数のインポートと関連 しています。もし 何か エラーが、ビュー関数のインポートする中で起きれば 、それは reverse() がエラーを返す原因になります。もし、ビュー関数が 逆引きしようとしたものでなかったときでさえもです。

URLconf ファイルの中でビューの参照が実在するのと、 正しくインポートされていることを確かにしてください。 参照するビューがまだ書かれていないのに、それを行に含まないでください。 なぜならそのビューはまだインポートできないからです。

Note

reverse() によって返される文字列は、 ref:URL クオート <uri-and-iri-handling> です。例えば:

>>> reverse('cities', args=[u'Orléans'])
'.../Orl%C3%A9ans/'

もっとエンコーディングを適用するには(urlquote()urllib.quote のように) reverse() のアウトプットは望ましくない 結果を引き起こします。

reverse_lazy()
リリースノートを参照してください

reverse() の簡単に使えるバージョン

reverse_lazy(viewname[, urlconf=None, args=None, kwargs=None, current_app=None])

これは、プロジェクトの URLConf がロードされる前に、 URL 逆引きを使う必要がある 場合に便利です。いくつかの一般的なケースではこの関数がまさに必要になります:

  • ジェネリッククラスベースビューをの url アトリビュートとして、 URL を逆引きして提供する場合
  • デコレータとして、逆引き URL を提供する場合( django.contrib.auth.decorators.permission_required() の デコレータのための login_url 引数のような)
  • 関数のシグネチャの中で、パラメータとしてデフォルトの値として 逆引きした URL を提供する場合。
resolve()

django.core.urlresolvers.resolve() 関数は、URL のパスとそれに付随してい るビュー関数を呼び出すのに使われます。

resolve(path, urlconf=None)

path はあなたがひも解きたい URL パスです。 reverse() と一緒で、 urlconf パラメータ に関して考える必要はありません。この関数が返すのは ResolverMatch オブジェクトで、これは解決された URL の様々なメタデータにアクセスできる クラスです。

もし、 URL がひも解かれなかったら、関数は Http404 という例外を発生させます。

class ResolverMatch
func

URL を提供するのに使われるビュー関数です。

args

これは、ビュー関数に渡されて、 URL からパースされた 引数です。

kwargs

このキーワード引数はビュー関数に渡され、 URL からパースされた 引数です。

url_name

URL にマッチした URL パターンの名前です。

app_name

アプリケーションの名前空間で、 URL にマッチした URL パターンの 為に使われます。

namespace

インスタンスの名前空間で、 URL にマッチした URL パターンの 為に使われます。

namespaces

インスタンスのフルの名前空間の、個別の名前空間の要素を リストにしたもので、 URL パターンにマッチした URL のために 用いられます。すなわち、もし名前空間が foo:bar であるなら、 このリストは ['foo', 'bar'] になります。

ResolverMatch オブジェクトは、 URL にマッチする URL パターンに ついての情報を提供し、それの問い合わせを行うことが出来ます。:

# Resolve a URL
match = resolve('/some/path/')
# Print the URL pattern that matches the URL
print match.url_name

ResolverMatch オブジェクトは、三つのものに対して割当てられます。:

func, args, kwargs = resolve('/some/path/')
三つの割当は、逆方向への互換性のためにあります。 resolve() は Django 1.3 では、 三つのものを含んで(ビュー関数と、引数、キーワード引数)いて、 ResolverMatch object (名前空間とパターン情報の提供も同じように) は DJango の前バージョンのものでは使えません。

一つの resolve() の使い方としては、 Http404 エラーが出ているかどうかを、リダイレクトする前にテストする ような用途です。:

from urlparse import urlparse
from django.core.urlresolvers import resolve
from django.http import HttpResponseRedirect, Http404

def myview(request):
    next = request.META.get('HTTP_REFERER', None) or '/'
    response = HttpResponseRedirect(next)

    #リクエストとレスポンスを必要に合わせて変更してください。
    #依存するローカルな環境のクッキーもセットしてください。

    view, args, kwargs = resolve(urlparse(next)[2])
    kwargs['request'] = request
    try:
        view(*args, **kwargs)
    except Http404:
        return HttpResponseRedirect('/')
    return response
get_script_prefix()
get_script_prefix()

通常、 reverse() を 使うか、 permalink() を URL を アプリケーション内で定義するために用います。しかし、もしアプリケーションが 自身の URL の階層の一部分を構成するのならば、 URL を生成する機会が必要です。 このようなケースで、 Web サーバー内の Django プロジェクトの ベース URL を 見つけられる必要があり(通常、 reverse() がこのような必要性を満たしてくれます)、この場合、 get_script_prefix() を呼ぶと、Django プロジェクトにへ URL スクリプトのプレフィクスを返します。 もし、 Django プロジェクトが Web サーバーのルートに存在するなら、 これは常に "/" になります、しかしこれは変更可能です、 django.root を使うことでインスタンスで出来ます。( Django と Apache と mod_python を見てください。)

ビューを書く

revision-up-to:17812 (1.4)

ビュー関数、あるいは単に ビュー とは、簡単にいえばウェブリクエストを引数 にとり、ウェブレスポンスを返す関数です。レスポンスはウェブページを表す HTML コンテンツでもよいし、リダイレクトでも、 404 エラーでも、 XML ドキュメント でも、画像でも、何でもかまいません。ビューの中には、レスポンスを生成する上 で必要なロジックを自由に書けます。ビューのコードは、 Python パス上にあるか ぎり、どこに置いてもかまいません。他にはなんの制限も「黒魔術」もありません。 慣習では、 プロジェクトディレクトリかアプリケーションディレクトリの下に views.py という名前のファイルを作成して、そこにビューのコードを置くこと になっています。

簡単なビュー

以下に示すのは、現在の日付や時刻を HTML 形式で返すビューの例です:

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

Let’s step through this code one line at a time:

  • まず、 django.http モジュールから HttpResponse クラスを import しています。 Python の datetime ライブラリもロードしておきます。

  • 次に、 current_datetime という関数を定義しています。これがビュー 関数です。ビュー関数は第一引数に HttpRequest オブジェクトを取ります。慣習的に、この引数の名前は request にして います。

    ビュー関数はどんな名前でもかまいません。Django にビュー関数として認識 してもらうために、何らかの特殊な命名規則に従う必要はないのです。ここ では、 current_datetime という名前を付けて、どんな機能のビューか 分かりやすくしています。

  • ビューは、生成したレスポンスコンテンツの入った HttpResponse オブジェクトを返します。ビュー関数 は HttpResponse オブジェクトを返さねばなりません (例外も返してよ いのですが、それについては後で説明します)。

Django のタイムゾーン設定

Django には TIME_ZONE 設定があり、デフォルト値は America/Chicago に設定されています。お住まいの地域がここでなければ、 変更しておいてください。

URL をビューにマップする

このビュー関数は、現在の日付と時刻が入った HTML ページを生成して返します。 何らかの URL でこのビューを表示したければ、 URLconf を作成する必要があり ます。 URL ディスパッチャ の説明を読んでください。

エラーを返す

Django では、簡単に HTTP エラーコードを返せます。エラー応答は、すでに述べた HttpResponseNotFound, HttpResponseForbidden, HttpResponseServerError といったサブクラスのインスタンスを作成して、 以下の例のように通常の HttpResponse の代わりに返すだ けです:

def my_view(request):
    # ...
    if foo:
        return HttpResponseNotFound('<h1>Page not found</h1>')
    else:
        return HttpResponse('<h1>Page was found</h1>')

全ての HTTP レスポンスコードごとに特別なサブクラスがあるわけではありません。 多くは共通化できるからです。しかし HttpResponse のドキ ュメントにあるように、 HttpResponse のコンストラクタに HTTP ステータスコードを渡すことで、好きなステータスコードを返すクラスを作れ ます。例:

def my_view(request):
    # ...

    # Return a "created" (201) response code.
    return HttpResponse(status=201)

ただし、 404 エラーは他の HTTP エラーよりはるかに良く使われるエラーなので、 もっと簡単に扱う方法を提供しています。

Http404 例外
class django.http.Http404

HttpResponseNotFound` のようなエラーを返す場合、以下 のように、エラーページの中身になる HTML を指定せねばなりません:

return HttpResponseNotFound('<h1>Page not found</h1>')

これはちょっと不便ですね。それに、サイト全体で一貫した 404 エラーページを用 意しておく方が賢明です。そこで、 Django には Http404 例外があります。 ビュー関数のどこかで Http404 例外を送出すると、 Django はこのエラーを捕 捉して、サイト標準のエラーページを HTTP エラーコード 404 で返します。

例を示しましょう:

from django.http import Http404

def detail(request, poll_id):
    try:
        p = Poll.objects.get(pk=poll_id)
    except Poll.DoesNotExist:
        raise Http404
    return render_to_response('polls/detail.html', {'poll': p})

Http404 例外を使うには、 404 エラーの送出時に表示されるテンプレートを作 成しておかねばなりません。このテンプレートの名前は 404.html で、テンプ レートツリーの一番上に配置せねばなりません。

エラービューのカスタマイズ
404 (ページが見つかりません) ビュー

Http404 例外を送出すると、 Django は 404 エラー処理用の特殊なビューをロー ドします。デフォルトでは、このビューは django.views.defaults.page_not_found に設定されています。 django.views.defaults.page_not_found404.html という名前のテンプ レートをロードしてレンダします。

このため、テンプレートディレクトリの再上階層に 404.html という名前のテ ンプレートを作成しておかねばなりません。このテンプレートは全ての 404 エラー に対して用いられます。デフォルトの 404 ビューはテンプレートに request_path という変数を渡します。これはエラーになった URL です。

page_not_found ビューは 99% の Web アプリケーションの要求を満たすはずで

すが、 404 ビューを自作したい場合には、URLconf で以下のようにして handler404 を指定します:

handler404 = 'mysite.views.my_custom_404_view'

舞台裏では、 Django はルート URLconf の handler404 を介して 404 ビューを 捜し出します。定義がなければ django.views.defaults.page_not_found にフォ ールバックします。

404 ビューについて、知っておかねばならないことが 4 つあります:

  • 404 ビューは、リクエストされた URL に対して、 Django が URLconf の全 ての正規表現を調べた結果、一致するものをみつけられなかった場合にも呼 び出されます。
  • 404 ビューを自作せず、ただデフォルトのビューを使う場合でも、一つだけ やらねばならないこととして、 404.html という名前のテンプレートを 作成せねばなりません。
  • 404 ビューに渡されるコンテキストは django.template.RequestContext なので、テンプレートには TEMPLATE_CONTEXT_PROCESSORS で追加した変数も 渡ります。 (例えば MEDIA_URL)
  • (settings モジュールで) DEBUGTrue にすると、 404 ビューは 呼び出されず、代わりにトレースバックが表示されます。
500 (サーバエラー) ビュー

404 エラーと同様に、 Django はビューのコード上で実行時エラーに遭遇した場合 の挙動も特別扱いしています。ビューを実行した結果、例外が送出されると、 Django はデフォルトで django.views.defaults.server_error というビューを 呼び出します。このビューは 500.html というテンプレートをロードしてレン ダします。

このため、テンプレートディレクトリの再上階層に 500.html という名前のテ ンプレートを作成しておかねばなりません。このテンプレートは全ての 500 エラー に対して用いられます。デフォルトの 500 ビューはテンプレートに一切変数を渡さ ず、空の Context インスタンスを渡してレンダリングを実行しますが、これは さらなるエラーが発生するのを防ぐためです。

server_error ビューは 99% の Web アプリケーションの要求を満たすはずです が、 500 ビューを自作したい場合には、URLconf で以下のようにして handler500 を指定します:

handler500 = 'mysite.views.my_custom_error_view'

舞台裏では、 Django はルート URLconf の handler500 を介して 500 ビューを 捜し出します。何も定義していなければ、 django.views.defaults.server_error にフォールバックします。

500 ビューについて、知っておかねばならないことが 2 つあります:

  • 500 ビューを自作せず、ただデフォルトのビューを使う場合でも、一つだけ やらねばならないこととして、 500.html という名前のテンプレートを 作成せねばなりません。
  • (settings モジュールで) DEBUGTrue にすると、 500 ビューは 呼び出されず、代わりにトレースバックが表示されます。
403 (閲覧が禁止されています) ビュー
リリースノートを参照してください

404 ビュー、 500 ビューと同じスタイルで、 Django は 403 Forbidden エラーを 処理するためのビューを持っています。ビューが 403 例外を起こしたら、 Django はデフォルトでは django.views.defaults.permission_denied ビューを呼びま す。

このビューは 403.html テンプレートを最上階層のテンプレートディレクトリ からロードし、レンダします。ファイルがない場合、 RFC 2616 (HTTP 1.1 仕様) により “403 Forbidden” テキストを返します。

404 や 500 ビューと同じように、 handler403 を URLconf に設定することで django.views.defaults.permission_denied をオーバーライドできます:

handler403 = 'mysite.views.my_custom_permission_denied_view'

ビューのデコレータ

Django には、HTTP の様々な機能をビューに適用するためのいくつかのデコレータ が用意されています。

HTTP メソッドの制限

django.views.decorators.http 内の以下のデコレータはリクエストメソッド によるアクセス制限に使用できます。このデコレータは、条件を満たしていない 場合 django.http.HttpResponseNotAllowed を返します。

require_http_methods(request_method_list)

特定のリクエストメソッドのみを許可するためのデコレータの利用方法です:

from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def my_view(request):
    # I can assume now that only GET or POST requests make it this far
    # ...
    pass

リクエストメソッドは大文字にする必要があります。

require_GET()

ビューに GET メソッドのみを許可するデコレータです。

require_POST()

ビューに POST メソッドのみを許可するデコレータです。

require_safe()
リリースノートを参照してください

ビューに GET と HEAD メソッドのみを許可するデコレータです。 これらのメソッドは、リクエストされたリソースを取得する意外の目的で使用 すべきではないので、一般的に “safe” (安全) と見なされています。

Note

Django は、HEAD リクエストのレスポンスからヘッダは変更せず、 コンテンツを自動的に削除するため、GET リクエストのビューと同様に処理 できます。リンクチェッカー等のソフトウェアは、 HEAD リクエストに依存しているので、 require_GET よりも require_safe を使うほうが良いでしょう。

ビューの条件処理

django.views.decorators.http 内の以下のデコレータは特定のビューの キャッシュ制御に使用できます。

condition(etag_func=None, last_modified_func=None)
etag(etag_func)
last_modified(last_modified_func)

これらのデコレータは ETagLast-Modified ヘッダの生成に 使用します。詳細については ビューの条件処理 を参照してください。

GZip 圧縮

django.views.decorators.gzip 内のデコレータはビュー単位でコンテンツの 圧縮を制御します。

gzip_page()

このデコレータは、ブラウザが gzip 圧縮に対応している場合に圧縮します。 Vary ヘッダをセットするので、 Accept-Encoding ヘッダに基づいてキャッシュに保管されます。

Vary ヘッダ

django.views.decorators.vary 内のデコレータにより特定のリクエストヘッダ に基づきキャッシュを制御できます。

vary_on_headers(*headers)

Vary ヘッダは、キャッシュメカニズムがキャッシュキーを生成する際に考慮 すべきリクエストヘッダを定義します。

詳細については Vary ヘッダを使う を参照してください。

ファイルアップロード

revision-up-to:17812 (1.4)

多くの Web サイトにとって、ファイルアップロードのサポートは不可欠ですね。 Django がアップロードされたファイルを扱うとき、ファイルデータは最終的に request.FILES に入ります (request オブジェクトの詳細は リクエスト/レスポンスオブジェクト のドキュメ ントを参照してください)。このドキュメントでは、ファイルがどのようにしてディ スクやメモリ上に保存されるかを説明し、そのデフォルトの動作をカスタマイズす る方法について説明します。

ファイルアップロードの基本

FileField を含む以下のような簡単なフォームを考えて みましょう:

from django import forms

class UploadFileForm(forms.Form):
    title = forms.CharField(max_length=50)
    file  = forms.FileField()

このフォームからの入力を扱うビューは、ファイルデータを request.FILES で受け取ります。 request.FILES は ファイルデータの入った辞書で、辞書のキーはフォームクラス中の FileField (または ImageFieldFileField のサブクラス) の名前です。従って、 上の例では、 request.FILES['file'] でファイルデータにアクセスできます。

もしリクエストメソッドが POST であり、尚且つ <form> において アトリビュート enctype="multipart/form-data" を含むリクエストが ポストされたのであれば、 request.FILES はデータのみを含んでいる事に注意して下さい。 そうでない場合 request.FILES は空です。

ほとんどの場合は、「 アップロードされたファイルをフォームに結びつける 」の節で説明した方法に従っ て、 request からデータを取り出してフォームに渡すだけでアップロードファ イルを処理できます:

from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
# アップロードファイルを処理する関数を import します
from somewhere import handle_uploaded_file

def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponseRedirect('/success/url/')
    else:
        form = UploadFileForm()
    return render_to_response('upload.html', {'form': form})

request.FILES を明示的にフォーム のコンストラクタに渡さねばならないので注意してください。 これはフォームにファイルデータを結びつけるために必要な手順です。

アップロードファイルの処理
class UploadedFile

パズルの最後のピースは request.FILES からの実際のデータファイルをハンドリングすることです。 この辞書の各エントリーはアップロードされたファイルをシンプルなラッパで 包んだ UploadedFile オブジェクトです。 通常、アップロードされた内容にアクセスする為にこれらのメソッドのいずれか を使う事になるでしょう:

read()

アップロードされたデータ全体をファイルから読み出します。このメソッ ドを使うときに十分注意してください。というのも、アップロードされた ファイルが巨大だと、メモリに読み込む際にシステムの容量を越してしま うかもしれないからです。そのようなときは、後述の chunks() を使う とよいでしょう。

multiple_chunks()

ファイルが大きくて、複数のチャンクに分けて読み出すべきである場合に True を返します。デフォルトの設定では、 2.5 Mbytes より大きなファ イルに対して True を返します。サイズの閾値は設定でき、後で説明 します。

chunks

ファイルのチャンクを返すジェネレータです。 multiple_chunks()True の場合には、 read() ではなくこのメソッドを使ってくださ い。

実際には、常に chunks() を使うのがよいでしょう。後述の例を参照 してください。

name

my_file.txt のような、アップロードされたファイルの名前です。

size

アップロードファイルのサイズ(単位バイト)です。

UploadedFile オブジェクトは他にもいくつかメソッドや属性を備えています。 詳しくは UploadedFile オブジェクト を参照してください。

まとめると、アップロードファイルの一般的な処理例は以下の通りです:

def handle_uploaded_file(f):
    destination = open('some/file/name.txt', 'wb+')
    for chunk in f.chunks():
        destination.write(chunk)
    destination.close()

上の例では、巨大なファイルを read() してシステムの容量を超えないように、 UploadedFile.chunks() を使っています。

アップロードされたファイルの保存先

アップロードされたファイルデータは、処理され保存される前に、システムのどこ かに一時的に記憶されていなければなりません。

デフォルトの設定では、アップロードされたファイルデータが 2.5 Mbytes より小 さければ、 Django はファイルデータ全体をメモリに保持します。そのため、ファ イルデータの保存処理はメモリからディスクへの書き込みだけで実現され、高速で す。

しかし、ファイルデータが大きすぎる場合、 Django はファイルデータをシステム のテンポラリディレクトリに一時ファイルとして保存します。従って、 *nix ライ クのプラットフォームでは、 Django は /tmp/tmpzfp6I6.upload のような ファイルを生成します。ファイルデータがとても大きければ、 Django がデータを ディスクにストリーム書き込みするにつれて、一時ファイルのサイズが増えてゆく のを観察できるでしょう。

2.5 Mbytes や /tmp といった仕様は、単に「妥当なデフォルト値」にすぎませ ん。アップロード時の挙動をカスタマイズしたり完全に置き換えたりしたければ、 この後の詳細説明に進んでください。

アップロードファイルハンドラの挙動を変更する

ファイルアップロードの挙動は、以下の 3 つの設定で制御できます:

FILE_UPLOAD_MAX_MEMORY_SIZE
アップロードされたファイルをメモリに保存する上限のサイズです。 設定値よりも大きなファイルがアップロードされると、ファイルデータは ディスクに書き込まれます。 デフォルト値は 2.5 Mbytes です。
FILE_UPLOAD_TEMP_DIR

FILE_UPLOAD_MAX_MEMORY_SIZE より大きなファイルがアップロー ドされたときにファイルデータを保存するディレクトリです。

デフォルト値はシステム標準の一時ファイルディレクトリです (UNIX ライクのシステムでは /tmp です)。

FILE_UPLOAD_PERMISSIONS

アップロードされたファイルに設定するファイルモードで、数字で表現 (例: 0644) します。ファイルモードの意味は os.chmod() のドキュメントを参照してください。

この値を設定しないか、 None にすると、アップロードされたファイ ルのモードはオペレーティングシステムに依存します。ほとんどのプラッ トフォームでは、一時ファイルのファイルモードは