revision-up-to: | 11321 (1.1) |
---|
revision-up-to: | 17812 (1.4) |
---|
Django を使いこなすための全てがここにあります。
和訳について
このドキュメントは、 Django の標準ドキュメント の和訳です。和訳は日 本 Django ユーザ会の有志の手でメンテナンスされています。和訳に関する問 い合わせは、 Django プロジェクトのトラッカではなく 、 django-ja メ ーリングリストにお寄せください。
困り事ですか?手を貸しましょう!
revision-up-to: | 17812 (1.4) |
---|
Django は初めてですか? Web 開発全般も?なら、ここに来て正解ですね。ここに あるドキュメントを読んで、行動に移してみましょう。
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
コマンドは利用可能な全てのモデルを探し、まだ作成されてい
ないテーブルがあれば作成します。
これだけで、制約のない充実した 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()
モデルを定義したら、 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 スキームは、高品質な 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 は他にもまだま だ便利な機能を備えています:
次は、あなたが Django をダウンロード して、 チュートリアル を読み、 コミュニティ に参加す る番です。ご精読ありがとうございました!
revision-up-to: | 17812 (1.4) |
---|
Django を動かすには、まずインストールせねばなりません。ここでは、 Django を 学んでみるにあたって必要な、簡単で最小限のインストール方法を示します。 色々なインストール方法をカバーしている 詳細なインストールガイド も用意してあります。
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 をインストールする場合、 新しいバージョンをインストールする前に、まず旧バージョンをアンインス トールしておく 必要があります。
3 通りの簡単な方法で、Django をインストールできます:
常に使用している Django のバージョンと一致するドキュメントを 参照するようにしてください。
上に挙げた選択肢のうち、最初の二つを選んだ場合は、ドキュメントを読む際 に「 開発版で登場した機能です 」とマークされた部分に気をつけてくだ さい。この文は、開発版の Django でのみ利用可能な機能をマークしています。 そうした機能は、公式リリースでは動かないでしょう。
Django が Python から使用できるか確認するために、シェル上で python
と
入力します。 Python プロンプトの中で、 Django を import してみます:
>>> import django
>>> print django.get_version()
1.4
これだけです。さあ、 チュートリアルをやってみましょう.
revision-up-to: | 17812 (1.4) |
---|
さあ、例を交えながら学んでゆきましょう。
このチュートリアルでは、簡単な投票 (poll) アプリケーションの作成に取り組ん でもらいます。
Poll アプリケーションは、
の二つの部分に分かれています。
Django は既にインストール済み として、説明を進めます。
Django がインストールされているかどうかは、Python 対話シェルを起動して
import django
を実行してみればわかります。エラーなく import できるなら、
Django はインストールされています。
困ったときは:
このチュートリアルを進めてゆく上で困ったことがあったら、
django-users や
irc.freenode.net
の
#djangoチャネル で誰か助けてくれそ
うな人と話してみてください。
初めて Django を使うのなら、最初のセットアップを行う必要があります。通常は、 Django の プロジェクト (project) を構成するコードを自動生成 します。プロジェクトとは、データベースの設定や Django 固有のオプション、ア プリケーション固有の設定などといった、個々の Django インスタンスの設定をあ つめたものです。
コマンドラインから、コードを置きたい場所に cd
して、以下のコマンドを
実行してください。
django-admin.py startproject mysite
現在のディレクトリに mysite
ディレクトリが作成されます。
ディストリビューションパッケージでスクリプトの名前が違うかも
もし apt-get や yum のような Linux ディストリビューションのパッケージ
マネージャを使って Django をインストールした場合、 django-admin.py
は django-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/bin
に
django-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 の設定 を参照してください。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
のリファレンスを参照して
ください。
それでは、 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 に付属のものです:
django.contrib.auth
– 認証システムです。django.contrib.contenttypes
– コンテンツタイプフレームワークです。django.contrib.sessions
– セッションフレームワークです。django.contrib.sites
– 一つの Django で複数のサイトを管理する
ためのフレームワークです。django.contrib.messages
– メッセージフレームワークです。django.contrib.staticfiles
– 静的なファイルを管理するための
フレームワークです。これらの機能はよく使われるのでデフォルトで付属しています。
上に挙げたアプリケーションは、必ず少なくとも一つのデータベーステーブルを使 います。そこで、アプリケーションを使う前にテーブルを作成しておく必要があり ます。テーブルを作成するには以下のコマンドを使います:
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`
インスタンスの名前 (question
や pub_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
文を実
行) できます。ただし、その前に 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;
以下の点に注意してください:
polls
) とモデルの小文字表記
(poll
および choice
) を使って自動的に生成されます (この挙動は
オーバライドできます。)"_id"
を追加します。も
ちろんこの挙動もオーバライド可能です。REFERENCES
文で明示的に作成されます。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 のドキュメント を読んで下さい。
さて、Python 対話シェルを起動して、 Django が提供する API で遊んでみましょ う。 Python シェルを起動するには、以下のコマンドを実行します:
python manage.py shell
単に “python” を実行しないのは、 Django に settings.py
ファイルへの
import パスを与える DJANGO_SETTINGS_MODULE
の環境変数を manage.py
で設定しているからです。
manage.py を使わずに済ませる方法
manage.py
を使いたくなくても、問題はありません。環境変数
DJANGO_SETTINGS_MODULE
を mysite.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 関係のモデルを少し修正して、 Poll
と Choice
に
__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 datetime
と from 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 が自動生成 する管理インタフェースを動かしてみましょう。
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 のログイ ン画面が表示されるはずです:
自分の画面と違う場合
もしこの時点で、上のログイン画面の代わりに以下のようなエラーの画面が 表示された場合は:
ImportError at /admin/
cannot import name patterns
...
おそらくこのチュートリアルのバージョンとは違う Django のバージョンを 使用していることでしょう。古いチュートリアルを参照するか、新しいバー ジョンの Django を入手してください。
さあログインしてみましょう。(チュートリアルその 1 で、スーパユーザのアカウ ントを作成したはずです。覚えていますか?) ログインしたら、管理サイトのインデ クスページが表示されるはずです:
「グループ (Groups)」や「ユーザ (Users)」、「サイト (Sites)」といった編集 可能なコンテンツが表示されるはずです。これらはデフォルトで Django に付属し ているコアの機能です。
ところで、 polls アプリケーションはどこにあるんでしょう? admin のインデ クスページを見ても表示されていませんね。
実は、まだ一つやるべきことが残っていました。 admin に Poll
モデルがイ
ンタフェースを持つことを教える必要があるのです。 polls
ディレクトリに
admin.py
ファイルを追加して、以下のように編集してください:
from polls.models import Poll
from django.contrib import admin
admin.site.register(Poll)
admin のページをリロードして、どんな変化が起きたか見てみましょう。通常、 開発サーバはプロジェクトを自動的にリロードしてくれるので、コードに加えた変 更はただちにブラウザで確認できます。
Poll
モデルを登録したので、 Django は Poll
を管理サイトに表示するよ
うになりました:
「Polls」 をクリックしてみてください。 チェンジリスト (change list) のペー
ジに入ります。このページはデータベース上の全ての Poll
オブジェクトを表
示していて、変更したいオブジェクトを選択できます。前のチュートリアルで作成
した 「What’s up」という Poll
オブジェクトがありますね。
「What’s up?」をクリックして編集してみましょう:
以下の点に注意してください:
DateTimeField
,
CharField
などの) 適切な HTML 入力ウィジェッ
トが対応しています。各タイプのフィールドには、 Django 管理サイトでデー
タを表示する方法が定義されています。DateTimeField
には JavaScript のショートカッ
トがついています。日付 (Date) のカラムには「今日 (Today)」へのショート
カットと、カレンダーのポップアップボタンがあります。時刻 (Time) のカラ
ムには「現在 (Now)」へのショートカットと、よく入力される時間のリストを
表示するポップアップボタンがあります。ページの末尾の部分には操作ボタンがいくつか表示されています:
もし「Date published」の値がチュートリアル 1 で作成した時間と違う場合は、
TIME_SONE
に現在のタイムゾーンの設定をし忘れた可能性があります。
変更し、リロードして正しい値が表示されるか確認してください。
「今日」や「現在」ショートカットをクリックして、「Date published」を変更し てみましょう。変更したら、「保存して編集を続ける」を押します。次に、右上に ある「履歴 (History)」をクリックしてみましょう。ユーザが管理サイト上でオブ ジェクトに対して行った全ての変更履歴を、変更時刻と変更を行ったユーザの名前 つきでリストにしたページを表示します:
しばらく操作してみましょう。これだけの機能をコードを書かずに実現したこ
とに驚くはずです。 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 の追加」フォーム は以下のようになります:
このフォームでは「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 を追加」ページをロードして、どんな表示になったか見てみましょう。開発 サーバをリスタートする必要があるかもしれません:
変わった点をみてみましょう。リレーション相手である Choice を表示するために
3 つのスロットがあります (extra
に指定した数ですね)。また、作成済みのオ
ブジェクトを「編集する」ページに行けば、いつでも 3 つ余分のスロットが表示さ
れるはずです。
さて、このコードにはちょっと問題があります。というのも、 Choice オブジェク
トを入力するためのフィールドを全部表示しようとすると、相当な広さのスクリー
ンが必要だからです。そこで、 Django にはテーブルを使ってインラインでリレー
ション相手のオブジェクトを表示するもう一つの方法があります。以下のように、
ChoiceInline
の宣言を変更してください:
class ChoiceInline(admin.TabularInline):
#...
StackedInline
に変わって TabularInline
を使うと、
リレーション相手のオブジェクトはよりコンパクトなテーブル形式で表示されます:
さあ、これで 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 のチェンジリストのページは以下のようになります:
カラムのヘッダをクリックすると、カラムの値に応じてエントリを並べ換えできま
す。ただし 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
フィールドの値に従ってフィルタできるようになります:
表示されるフィルタのタイプは、フィルタに使うフィールドのタイプによって変わ
ります。 pub_date
は DateTimeField
なので、 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 アプリケーショ ンの公開用ビュー作成にとりかかりましょう。
revision-up-to: | 17812 (1.4) |
---|
このチュートリアルは チュートリアルその 2 の続き です。ここでは、引続きWeb 投票アプリケーションの開発を例にして、公開用のイ ンタフェース、ビュー(view) の作成を焦点に解説します。
ビューとは、 Django のアプリケーションにおいて特定の機能を提供するウェブペー ジの「型 (type)」であり、独自のテンプレートを持っています。例えばブログアプ リケーションなら、以下のようなビューがあるでしょう:
Poll アプリケーションの場合には、以下の 4 つのビューを作成します:
Django では、各ビューは簡単な Python の関数として表現されます。
ビューを書く上での最初のステップは、 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.py
の
TEMPLATE_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” とい う投票項目の入ったブレットリストを表示します。リンクは詳細ページを指します。
テンプレートをロードしてコンテキストに値を入れ、テンプレートをレンダリング
した結果を 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})
この作業によって、 loader
や
Context
、 HttpResponse
を
import する必要はなくなりました。
render_to_response()
関数は第一引数にテンプレートの
名前をとり、オプションの第二引数に辞書をとります。
render_to_response()
はテンプレートを指定のコンテキ
ストでレンダリングし、 HttpResponse
オブジェクトにし
て返します。
さて、今度は 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()
を実行し、該当オブジェクトがな
い場合には 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
を送出します。
ビュー内で Http404
を送出すると、 Django は 404 エラー
処理用の特殊なビューをロードします。このビューは URLconf にある
handler404
という変数 ( URLconf にあるもののみ有効です。その他に設定さ
れたものは影響がありません) を参照して見つけます。変数は URLconf のコール
バックで使っているのと同じ、 Python のドット表記法で表した関数名の文字列で
す。 404 ビュー自体に特殊なところはありません。単なる普通のビューです。
普通はわざわざ苦労して 404 ビューを書く必要はありません。もし handler404
を設定しなかった場合は、ビルトインビューである
django.views.defaults.page_not_found()
がデフォルトで使用されます。
この場合、 404.html
テンプレートをテンプレートディレクトリのルートに
作成する必要があります。デフォルトの 404 ビューはこのテンプレートを全ての
404 エラーに対して使用します。もし (設定モジュールで) DEBUG
を False
に設定していて、なおかつ 404.html
を作成しなかった場合、
Http500
が代わりに送出されます。 404.html
を作成することを覚えて
おいてください。
404 ビューに関するいくつかの注意すべき点:
DEBUG
を True
に設定している場合、
Django は 404 ビューを使わず、トレースバックを表示します
(404.html
も使われません)。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 を編集 していくうちに、実はかなり冗長な部分があることに気づくかもしれません:
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)),
)
ところで、すこし時間を割いて 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/” にアクセスすると、次のように処 理されます:
'^polls/'
へのマッチを検出します。"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) の使い方を学びましょ う。
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>
簡単に説明しましょう:
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
タグのループが何度実行されたかを
表す値です。{% 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 を返します。 3
は p.id
の値です。リダイレクト先
の URL は 'results'
ビューを呼び出し、最終的なページを表示します。
(プレフィクスを含めた) ビューの完全な名前を指定せねばならないので注意
してください。
チュートリアルその 3 で触れたように、
request
は HTTPRequest
オブジェクトです。
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})
テンプレート名が違うことだけを除き、
チュートリアルその 3 の detail()
とほとんど同
じですね。この冗長さは後で修正することにします。
今度は 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/
を表示して、投票してみましょう。票を入れるた
びに、結果のページが更新されていることがわかるはずです。選択肢を選ばずにフォー
ムを提出すると、エラーメッセージを表示するはずです。
チュートリアルその 3 の detail()
と
results()
という二つのビューはバカバカしいくらいに単純で、先程も述べた
ように冗長です。(これまた チュートリアルその 3 の)
Poll のリストを表示する index()
ビューも同様です。
こうしたビューは、基本的な Web 開発においてよくあるケース。すなわち、URL を 介して渡されたパラメタに従ってデータベースからデータを取り出し、テンプレー トをロードして、レンダリングしたテンプレートを返す、というケースを体現して います。これはきわめてよくあるケースなので、 Django では「汎用ビュー (generic view)」というショートカットのシステムを提供しています。
汎用ビューとは、よくあるパターンを抽象化して、 Python コードすら書かずにア プリケーションを書き上げられる状態にしたものです。
これまで作成してきた polls アプリケーションを汎用ビューシステムに変換して、 コードをばっさり捨てられるようにしましょう。変換にはほんの数ステップしかか かりません。そのステップとは:
です。詳しく見てゆきましょう。
なぜ今更コードを入れ換えるの?
一般に 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'),
)
この例では二つの汎用ビュー、
ListView
と
DetailView
を使っています。こ
れらのビューはそれぞれ、「オブジェクトのリストを表示する」および「あるタイ
プのオブジェクトの詳細ページを表示する」という二つの概念を抽象化しています。
model
パラメタによって提供されます。DetailView
汎用ビューには、
"pk"
という名前で URL から プライマリキー をキャプチャ
して渡すことになっています。そこで、汎用ビュー向けに poll_id
を
pk
に書き換えてあります。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
テンプレートを使用するように伝えます。
このチュートリアルの前の部分では、 poll
や latest_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,)))
サーバを実行して、新しく汎用ビューベースにした投票アプリケーションを使って みましょう。
汎用ビューの詳細は 汎用ビューのドキュメント を参照してく ださい。
revision-up-to: | 17812 (1.4) |
---|
さあ、ここにきたあなたは、 入門編 をあらかた読み終えて、 Django を使い続ける決心をしたことだと思います。入門編では、 Django のほんの 表面的な部分に触れただけにすぎません (実際、入門編の分量は、全体のドキュメ ントの 10% にも満たないのです)。
さて、次は何をしましょうか?
そうですね、私達は実践を通して学ぶのがとても大好きですよね。今や、読者のみ なさんは自分のプロジェクトを作成して、いろいろと遊んでみるのに十分な段階に あります。新しい技を身に付けたければ、いつでもここに戻って来てください。
私達は、 Django ドキュメントを有意義で、読みやすく、可能な限り完璧にしよう と努力しています。このドキュメントの残りの部分では、読者の皆さんが Django のドキュメントをできるだけ活用できるように、ドキュメントがどういう仕組みに なっているのかを説明しています。
(そう、これはいわばドキュメントのドキュメントです。ただし、このドキュメント の読み方を説明するドキュメントを書いたりはしませんから心配しないでください ね。)
Django には、いまや 200,000 語を越す たくさん のドキュメントがあります。 その中から必要なものを捜し出すのは大変です。そういうときは、 Search Page や Index から辿るとよいでしょう。
もちろん、片っ端から読み進むのもいいですけどね。
Django のドキュメントは、用途に応じていくつかの部に分かれています:
トピック別ガイド では、 Django の個々の構成要素 について詳しく解説しています。このセクションでは、 Django の モデルシステム, テンプレートエンジン, フォームフレームワーク といったトピックを解説しています。
おそらく、読者の皆さんはこのセクションを読み進むのに多くの時間を費す でしょう。このガイドを全て読破したら、 Django を扱う上で必要なことは ほとんど知っているはずです。
ウェブ開発に必要な知識は、いくつもの領域にまたがって広く、浅く分布し ているものです。 このセクションには、「〜をするにはどうしたらよいです か?」といった質問に答える、 HOWTO が書かれて います。例えば、 Django で PDF を生成する方法 や、 テンプレートタグを自作する方法 などです。
よくある質問は、これとは別に FAQ で扱っています。
Django のコードベースが毎日のように開発と改良を重ねているように、ドキュメン トも常に改良を重ねています。ドキュメントの改良は以下のような理由に基づいて 行われます:
Django のドキュメントはコードと同じソースコード管理システム下にあり、 Subversion リポジトリの django/trunk/docs ディレクトリ以下に置かれていま す。各ドキュメントは、例えば「汎用ビュー」フレームワークや、データベースモ デルの構築方法といった具合に、個別のトピックごとに別々のテキストファイルに なっています。
Django のドキュメントを入手するにはいくつか方法があります。おすすめの順に以 下に示します:
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 ドキュメントのローカルコピーを手に入れられま す:
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
docs/_build/html
に生成されます。Note
Django ドキュメントは Sphinx バージョン 0.6 以上を使って生成できます が、 Sphinx 1.0.2 以上を使用することをおすすめします。
前述したように、 Subversion リポジトリに入っているテキストドキュメントは 変更や追加によって「最新最良」の状態にあります。変更によって、開発版、すな わち Subverion (“trunk”) 版の Django に新たに登場した機能がテキストに記載さ れることがよくあります。このため、 Django の各バージョン間で一貫したドキュ メンテーションポリシをここで示しておきます。
我々は、以下のポリシに従っています:
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 もチェックしてみて ください。
revision-up-to: | 17812 (1.4) |
---|
このセクションでは、Django を使う上での鍵となる要素について解説しています。
revision-up-to: | 17821 (1.4) unfinished |
---|
このドキュメントを読めば Django を動かせるようになります。
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;
単に 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
で一度テーブ
ルを生成してしまえば、 syncdb
は ALTER TABLE
文を発行しません。
テストフレームワーク を使ってデータベースクエリのテ ストを行うのなら、データベースを生成する権限も必要です。
以前のバージョンからアップグレードする形で 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/
ではありません。
インストール方法の説明は、 OS ごとのインストールパッケージを入れる場合、公 式リリース版を入れる場合、そして最新の開発バージョンを入れる場合で少し異な ります。
どの方法を選ぶにしても、そんなに難しくはないので安心してください。
ディストリビューション固有の注意 を調べて、自分 のプラットフォーム/ディストリビューションで公式の Django パッケージやインス トーラが提供されていないか調べます。ディストリビューション固有の配布パッケー ジは、たいてい依存関係のあるパッケージを自動的にインストールしてくれたり、パ スの更新を行ってくれたりします。
pip
で¶これはDjangoをインストールする上でおすすめの方法です。
pip をインストールします。最も簡単な方法は、
standalone pip installer を使うことです。もし、 pip
がインストー
ルされているディストリビューションなら、古いものであればアップデートす
る必要ががあるかもしれません。(もし古いものであれば、インストールがう
まく動きません)
(オプション) virtualenv と virtualenvwrapper をみてみましょう。 これらのツールは、個別の Python 環境を構築することができます、 システム全体にパッケージをインストールするよりも実用的です。 これらは、管理者権限なしでもパッケージをインストールできます。 これらのツールを学んだり使うかを決めるのはあなた次第です。
もし Linux や MacOsX か、ほかの Unix 系統のOSを使用している場合、
sudo pip install Django
をシェルコマンドで入力します。
Windows を使っているならば、コマンドシェルを管理者権限で起動し、
pip install Django
コマンドを走らせます。
これらで、Python インストールのなかの site-packages
にDjangoが
インストールされます。
もし、あなたが virtualenv を使っているなら、 sudo
やadministrator
(管理者)の権限を使うことなく、virtualenv の site-packages
ディレクトリに Django がインストールされます。
tar xzvf Django-NNN.tar.gz
)。 Windows を使っているのなら、
コマンドラインツール bsdtar を使うか、 7-zip のような GUI ベースの
ツールを使います。cd Django-NNN
)sudo python setup.py install
を入力
します。 Windows を使っているのなら、管理者権限でコマンドプロンプト
を起動して、コマンド setup.py install
を実行します。
上のコマンドを実行すると、 Django は Python インストールディレクトリの
site-packages
ディレクトリ下にインストールされます。Django の開発版を追いかける
最新版の Django を使うと決めたのなら、 開発の進行状況 によく注意して、 以前のバージョンと互換性のない変更 がないか気をつけておく必要がある でしょう。これらのリンク先のページに注意していれば、使ってみたい新たな 機能や、手元の Django をアップデートする際に、自分のコードのどこを修正 する必要があるか追いかけられます。 (安定版では、 Django のアップデート 時に必要な変更は全てドキュメントに記載されています。)
Django のコードを更新して最新のバグフィクスや改良を適用したいのなら、以下の 説明に従って開発バージョンをインストールしてください。
Subversion, Git, Mercurial がインストールされていて、シェルからコマンドを実行できること
を確認してください。 (調べるには、シェルプロンプトで svn help
、
git help
, hg help
を入力します。)
Subversionリポジトリは、公式のGitとMercurialリポジトリ自体が最新のものとな
なるような標準のソースということを覚えておいてください。
以下のようにして、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
これらのミラーは五分ごとに最新版にアップデートされます。 けれど、サービス上にホストされてから常に五分ごとにアップデートされている という保証がついているわけではないので注意してください。
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 はどこにあるの?
を参照してください)
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) |
---|
モデルとは、サイトを構成するデータの、唯一絶対的なデータソースを指します。 モデルには、保存したいデータを表すデータフィールドと、データのビヘイビアを 定義します。通常、一つのモデルは一つのデータベーステーブルに対応しています。
モデルの基本として、以下のことを知っておいてください:
django.db.models.Model
のサブ
クラスです。以下の例では、 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_name
と last_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.myapp
は manage.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
など) の決定<input type="text">
,
<select>
など) の決定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
のフィー
ルドが必要です。
ForeignKey
と
ManyToManyField
と
OneToOneField
を除くフィールドクラスは、
第一引数に表示用のフィールド名を指定できます。この引数は省略可能です。引数
を指定しない場合、 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
は (Topping
に
ManyToManyField
の pizzas
をもたせる
のではなく) Pizza
に入れておきます。というのも、「トッピングを乗せるピ
ザ」を編集するよりも「ピザの上に乗せるトッピング」を編集する方が自然だから
です。というわけで、上の例のようにすれば、管理サイトの Pizza
の編集画面
上でユーザにトッピングを選ばせられます。
See also
ManyToManyField
フィールドも
モデルフィールドリファレンス で説明して
いる多くの引数を受け取ります。これらのオプションはリレーションがどの
ように動くべきかを定義するのに役立ちます。必須ではありません。
ピザとトッピングを組み合わせたり照合したりするだけのような多対多のリレーショ
ンを扱いたいのなら、標準の ManyToManyField
で事
足ります。しかし、時として、二つのモデルのリレーションそのものにデータを関
連づけたい場合があります。
例えば、ミュージシャンのグループと、在籍しているミュージシャンを追跡するア
プリケーションのケースを考えてみましょう。これは個人 (person) とグループの
関係なので、 ManyToManyField
でリレーションを表
現できるはずです。しかし、ミュージシャンとグループの関係には、あるミュージ
シャンがあるグループにいつ合流したか、のような細かい情報がたくさんあります。
こうした状況に対応するために、Django には、あらかじめリレーションを管理する
ためのモデル (中間モデル) を定義しておき、その中間モデルに向けてエクストラ
フィールド (extra field) を組み込んで多対多のリレーションを定義する方法があ
ります。中間モデルは ManyToManyField
の
through
引数に指定します。ミュージシャン
の例は以下のように書けます:
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
と違って、 add
や create
,
(beatles.members = [...]
のような) 代入では、リレーションの定義は行えま
せん:
# 使えません。
>>> beatles.members.add(john)
# これも使えません。
>>> beatles.members.create(name="George Harrison")
# これもまた使えません。
>>> beatles.members = [john, paul, ringo, george]
なぜでしょうか。中間モデルでリレーションを作成するときには、単に Person
と Group
の間のリレーションだけではなく、 Membership
テーブルのレコー
ドを生成するための情報が全て必要だからです。単に add
や create
を呼
び出したり代入を行うだけでは、リレーション以外の情報を埋められません。その
結果、中間モデルを使う場合は、 add
や create
は使えないのです。
中間モデルを使っている場合、多対多のリレーションを保存するには、中間モデル
のインスタンスを生成するしかない、ということを覚えておいてください。
同じ理由で 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 がモデルのフィールド名に課している制約は二つしかありません:
フィールド名は Python の予約語であってはなりません。さもないと Python のシンタクスエラーを引き起こすからです。
悪い例:
class Example(models.Model):
pass = models.IntegerField() # 'pass' は予約語です!
フィールド名には二つ以上の連続するアンダースコアを入れてはなりません。 なぜなら、Django は二重アンダースコアをクエリ照合構文で使っているか らです。
悪い例:
class Example(models.Model):
foo__bar = models.IntegerField() # 二重アンダースコアがあります!
データベースのカラム名はフィールドの名前と一致していなくてもよいので、デー
タベース側では制約を回避できます。詳しくは後述の db_column
を
参照してください。
join
や where
, select
のような SQL の予約語を、モデルのフィール
ド名に使っても かまいません 。というのも、Django は SQL クエリを生成する
ときにデータベーステーブル名やカラム名を常にエスケープするからです。エスケー
プにはデータベースエンジン固有のクオート方式を使います。
既存のモデルフィールドが目的とするアプリケーションに合わない場合や、あまり 一般的でないデータベースカラムタイプを活用したい場合のために、独自のフィー ルドクラスを作成できます。カスタムフィールドの詳しい作成方法は 「 howto-custom-model-fields 」で説明しています。
モデルにメタデータを指定するには、以下のようにモデルの内部クラス 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_delete
と
post_delete
シグナルのいずれかまたは
両方を使えば良いでしょう。
もう一つよくあるパターンは、カスタムの SQL 文をモデルメソッドやモジュールレ ベルのメソッドにに書くというものです。詳しくは、 「 素の SQL を扱う 」を参照してください。
Django のモデルクラス継承は、 Python の通常のクラス継承とほぼ同じように動作 します。モデル継承を実現するには、一つだけきめておかねばならないことがあり ます。それは、親クラスを (独自のデータベーステーブルを持った) 独立のモデル にしたいか、親クラスを共通情報の単なるホルダとして使い、子クラスでその情報 を扱う形にしたいかです。
Django には 3 種類のモデル継承スタイルがあります。
抽象ベースクラスは、たくさんのモデルに共通する情報を入れておきたいときに便
利な仕組みです。抽象ベースクラスを定義するには、ベースクラス定義の、
Meta に abstract=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: ...>
ただし、上の p
が Restaurant
クラスで ない のなら (Place
から
直接生成されたオブジェクトや、他のクラスの親クラスなら) p.restaurant
はエラーを引き起こします。
Meta
とマルチテーブル継承¶マルチテーブル継承では、子クラスが親クラスの Meta ク ラスを継承する意味がありません。 Meta オプションは全 て親クラスだけに適用されるべきもので、他のクラスに同じ内容を適用しても、矛 盾した振る舞いを引き起こすだけです(これは、独自のテーブルを持たない抽象ベー スクラスと対照的な部分です)。
従って、マルチテーブル継承の子クラスは親クラスの Meta
クラスにアクセスする方法を持ちません。ただし、限られたケースで、子クラスが
親クラスの挙動を継承する場合があります。それは、子クラスで
ordering
または
get_latest_by
属性を指定していない場合に、
親クラスの属性を継承するというものです。
親クラスで整列カラムが指定されていて、子クラス側で整列カラムを指定したくな い場合は、明示的設定を無効にしてください:
class ChildModel(ParentModel):
...
class Meta:
# 親クラスの整列カラム設定の効果を除去する
ordering = []
マルチテーブル継承は親クラスと子クラスを暗黙の
OneToOneField
でリンクするので、前述の例のように、
親クラスから子クラスをたどれます。しかし、子クラスをたどるときに使う名前は、
ForeignKey
や
ManyToManyField
リレーションを定義したと
きのデフォルトの related_name
と同じ値
を使っています。従って、子クラスで
ForeignKey
や
ManyToManyField
を別のクラスに向けて張る
ときには、 必ず related_name
をフ
ィールドに指定せねばなりません。指定し忘れると、 Django は
validate
や syncdb
時にエラーを送出します。
例えば、上の 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 つのオプションが存在しています。
一般的なルールとして:
Meta.managed=False
にして
ください。このオプションはデータベースのビューとテーブルを Django の
コントロール下に入れずにモデリングするのに役立ちます。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()
メソッドには戻り値がありません。
すでにデータベース上にあるオブジェクトへの変更を保存するには
save()
を使います。
Blog
インスタンス b5
がすでにデータベース上にあるとすると、以下の例
は b5
の名前を変更して、データベース上のレコードを更新します:
>> b5.name = 'New name'
>> b5.save()
この例では、背後で UPDATE
SQL 文が実行されています。 Django は明示的に
save()
を呼び出すまでデータベースを操作しません。
ForeignKey
や ManyToManyField
の保存¶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`
文であり、
フィルタは WHERE
や LIMIT
のような限定節にあたります。
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
オブジェクトが入っています。
テーブルからオブジェクトを取得する最も単純な方法は全てのオブジェクトを取得
するというものです。全オブジェクトを取得するには、
Manager
の
all()
メソッドを使って下さい:
>>> 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
です。
もし、クエリにマッチするのが一つだけだと分かるなら、
Manager の get()
メソッドが使えます。
これはオブジェクトを直接的に返します。:
>>> 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
istartswith
や iendswith
もあります。ここに挙げたのはほんの一部です。全ての照合タイプの解説は フィールド照合タイプのリファレンス を参照してくださ い。
Django では、背後で自動的に SQL JOIN
を処理し、照合の際にリレーションを
「追跡」する、強力でありながら直感的な手段を提供しています。リレーションを
またぐには、二重アンダースコアを使ってリレーションの張られたフィールドのフィー
ルド名を指定します。リレーション間のスパンは、目的のフィールドに到達するま
でいくらでも連鎖させられます。
以下の例では、 name
が 'Beatles Blog'
であるような Blog
の
Entry
エントリオブジェクト全てを取得します:
>>> 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
モデルは存在するものとします) entry
の author
を空にし
ていると、 Django は author
が存在しないために name
を辿れませんが、
エラーは送出せず、あたかも name
も空であるかのように扱います。たいてい
は、この仕様で期待通りに動きますが、 isnull
を使うと、少し混乱をきたし
ます。すなわち:
Blog.objects.filter(entry__author__name__isnull=True)
は、 author
の name
が空の Blog
だけでなく、 entry
の
author
が空の Blog
も返してしまうのです。後者のオブジェクトを返して
欲しくないのなら、以下のように書いてください:
Blog.objects.filter(entry__author__isnull=False,
entry__author__name__isnull=True)
ManyToManyField
や
ForeignKey
の逆リレーションを使ってフィルタを行
う場合、 2 種類の異なるフィルタ方法があります。 Blog
/ Entry
のリレー
ション (Blog
から Entry
が 1 対多であるようなリレーション) を考えて
みましょう。このようなモデルでは、例えばヘッドラインに ‘Lennon’ を含み、
かつ 2008 年に公開されたエントリを持つようなブログを検索したい場合があ
るでしょう。あるいは、ヘッドラインに ‘Lennon’ を含むか、 あるいは
2008 年に公開されたエントリを含むようなブログの検索もあり得ます。複数のエン
トリが一つの Blog
に関連付けられているので、いずれのクエリも実行可能で
すし、意味をなす場合があるでしょう。
ManyToManyField
を使っている場合にも、
同じような状況が起こります。例えば、 Entry
に tags
という名前の
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))
利便性のために、 Django には pk
という照合形式があります。 pk
は
primary_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
を使う 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] # キャッシュの値を再利用します。
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()
をカスタマイズしてい
て、オブジェクトを削除するときに呼び出したいのなら、
QuerySet
の
delete()
メソッドで一括削除するのではなく、直接
( QuerySet
の各要素を
取り出して逐次 delete()
を呼ぶなどして) 「手動で」インスタンスを削除せ
ねばなりません。
Django は、オブジェクトを削除する際に、通常 SQLでいう
ON DELETE CASCADE
制約をエミュレートします。すなわち、
削除対象のオブジェクトを指すような外部キーを持つ全てのオブジェクトも
同時に削除されるのです。:
b = Blog.objects.get(pk=1)
# 次の命令は、 Blog と Blog を指す Entry 全てを削除してしまいます。
b.delete()
ForeignKey
の
on_delete
引数を通して変えることが出
来ます。delete()
は
QuerySet
のメソッドにすぎず、
Manager
自体には公開
されていないので注意してください。これは誤って Entry.objects.delete()
を実行して 全ての エントリを削除してしまわないようにするための安全機構で
す。本当に全てのオブジェクトを削除 したい のなら、以下のように明示的に全
てのオブジェクトを表すクエリセットをリクエストしてください:
Entry.objects.all().delete()
モデルインスタンスをコピーする組み込みのメソッドは提供されていません、
ですが全てのフィールドとバリューを同一にした新しいインスタンスを簡単に作成する
ことができます。最もシンプルなケースでは、 pk
を None
にすることができ
ます。ブログの例を使うと:
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
継承の働きによって、 pk
と id
を None にしなくてはいけません:
django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4
このプロセスはひもづいているオブジェクトをコピーしません。もし、関係性のあるも
のもコピーしたいのならば、もう少し多めにコードを書かなければいけません。
Entry
が Author
に 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_save
や post_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'))
Django のデータベースマッパで扱うには複雑すぎる SQL 文を書かねばならないよ うな場合には、生の SQL 文実行モードを使えます。Django は生 SQL 文をクエリ とうる方法を二つ持っています。 素の SQL クエリを直接実行する を見てください。
生 raw-SQL 文実行モードの使い方としてお勧めなのは、そのようなクエリ文を実行 するモデルのカスタムメソッドやカスタムマネジャのメソッドを実装するというも のです。 Django はモデルレイヤでデータベースクエリを記述するように 何ら要求してはいません が、このアプローチをとることで、データアクセスのた めのロジックを一箇所にまとめられるので、コード組織化の観点からスマートにな ります。詳しい説明は topics-db-sql を参照してください。
最後に、 Django のデータベースレイヤは単にデータベースへの一つのインタフェー スに過ぎないということに注意しておきましょう。データベースには他のツールや プログラム言語、データベースフレームワークを介してアクセスできます。データ ベースについて Django 固有の何かがあるわけではないのです。
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
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')}
サマリ値を生成する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()
呼び出しさえも可能です。
ここまではクエリを受けるモデルに帰属するフィールドに対して集約することに 関して扱ってきました。しかし、集約したい値が、クエリを受けるモデルの関連 するモデルに属することもあります。
集約関数の中で集約したいフィールドを指定するときには、 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'))
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 |
---|
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
は、システム上の全てのオブジェクトを返しま
す。例えば、以下のモデル:
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 がカスタムマネジャと モデル継承 をどのように扱うか説明します:
これらのルールはモデルのグループに対してカスタムマネジャのコレクションを インストールしたいときに十分なフレキシビリティを提供しています。 例えば、このようなベースクラスがあるとします
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()
の結果をフィルタするマネジャは自動マネジャとしては
ふさわしくありません。
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
リストを使う限りは
攻撃から守られます。
時々 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呼び出しをするときは、 Djangoは現在のトランザクションを自動的に ダーティとしてマークします。これらの呼び出しを含むトランザクションが 正確に閉じたことを確認する必要があります。 詳細に関しては、 Djangoのトンランザクション処理に求められるもの を参照してください。
リリースノートを参照してください
Django 1.3より前では、 素のSQL呼び出しを使う際、
transaction.set_dirty()
を使って手動で トランザクションを
ダーティーとマークする必要がありました。
connection
と cursor
は PEP 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
を実行します。暗黙のロールバックは行いません。
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.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_MANAGEMENT
を True
に設定すれば、全ての自動トランザクション管理を無効にし、自分で
トランザクションを管理できます。
この場合、 Django はいかなるトランザクション管理も行わなくなります。ミドル ウェアが非明示的にトランザクションを commit することはなくなり、自分でロー ル管理を行わねばなりません。さらに、何らかのミドルウェアで変更の commit を 自分で行わねばならなくなります。
従って、トランザクションの無効化は、自作のトランザクション制御ミドルウェア を実行したい場合や、本当に変わったことをやりたい場合向けです。ほとんどの状況では、 デフォルトの挙動かトランザクションミドルウェアで十分で、必要に応じ て特定の関数だけを変更すればよいでしょう。
セーブポイントはトランザクションの中で、全てのトランザクションではなく、 トランザクションの一部をロールバックできるようにします。セーブポイントは PostgreSQL 8 や Oracle 、MySQL (バージョン 5.0.3 とそれより新しいもので InnnoDB をストレージエンジンに使っているもの)バックエンドで使えます。 他のバックエンドはセーブポイントを関数で提供します、しかしこれは空の オペレーションです – これは実際何もしません。
セーブポイントは、通常の autocommit
を Django で使っている限りこれは
役に立ちません。もし、 commit_on_success
か commit_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 のバージョンと使っているテーブルの形式に応じ て、テーブルがトランザクションをサポートする場合としない場合があります。 (「テーブルの形式」とは、”InnoDB” や “MyISAM” などを指します。) このドキュ メントでは MySQL のトランザクションにまつわる説明はしませんが、 MySQL のサ イトには トランザクションに関する情報 が掲載されています。
MySQL 構成がトランザクションをサポート していない 場合、 Django は自動コ ミットモードで動作します。すなわち、 SQL 文は呼び出されたその場で実行され、 コミットされます。 MySQL 構成がトランザクションをサポートしている場合、 Django はこのドキュメントの説明通りにトランザクションを処理します。
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
これは、 オートコミットデコレータ
と同じではなく、データベースレベルでのオートコミットを使う時に、それは
データベースのトランザクションは全くありません。 オートコミット
でコレーッタはトランザクションを使い、データベースが変化する処理が生じた
時に、自動的にそれぞれのトランザクションをコミットします。
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)¶model
が db
で与えられたデータベースと同期されるべきかどうかを判別し
ます。モデルが同期されるべきとき 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 が今のところ扱えない参照整合性の問題を紹介しています。
記載されているマスター/スレーブ構成にも欠陥があります。応答の遅延を扱う上で、 なんの解法も提供しません (つまり、クエリ不一致によって、スレーブに伝播させる ための書き込みに時間が取られるからです) 。
実際どんな意味があるのでしょう? myapp
が other
データベースに存在し、
その他のモデルは master
、 slave1
、 slave2
間の、マスター/スレーブ
構成に存在して欲しいとしましょう。これを実現するには、 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
設定に
記述されている順序で問い合わせされます。この例では MyAppRouter
が
MasterSlaveRouter
よりも前に処理されます。結果として、 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 は、マルチデータベースの明示的なサポートはしていません。ルータ
チェインによって指定された他のデータベス上のモデルのため、 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 つめのサイトでは、
Author
、 Publisher
オブジェクトが見られます。 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 のデータベース層は、開発者がデータベースから多くを得ることを助ける色々 な方法を提供しています。このドキュメントは関連するドキュメントのリンクを集めて 色々なヒントを加え、データベース利用の最適化を試みる時に踏むべきステップを概略 する多くの見出しのもとに整理しています。
一般的なプログラミングの実践として、これは言うまでもないことです。 実行されるクエリと実行にかかるコスト を見てく ださい。また、 django-debug-toolbar のような外部のプロジェクトや、データベー スを直接モニタするツールを使っても良いでしょう。
必要に従って、速度を最適化するのか、メモリを最適化するのか、または両方なのかを 思い出してください。時には一方の最適化が他方にとって有害なものになりますが、ま た時には互いに助けになります。また、データベースプロセスでの処理は、 Python プ ロセスで行われる同じ量の処理と同じコストにならないかもしれません。優先度がどう か、バランスを取るべきか、全ての計測が必要か、を決定するのはあなたです。これら はアプリケーションとサーバに依存するでしょうから。
以下の全てにおいて、変更が利益を生んでいることを確かめるため、そして大きすぎる 利益がコードの可読性を下げていないかを確かめるため、変更を行うごとに計測するの を忘れないようにしてください。以下の示唆 ** 全て ** について言えることですが、 あなたの環境では一般的な原則が当てはまらないかもしれない、もしかすると逆かもし れないということに注意してください。
以下が含まれます:
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() # クエリがまた実行された
テンプレートコードを読む時には注意してください。テンプレートシステムはカッコを 使うことを許容しませんが、上記の区別なく呼び出し可能オブジェクトを自動的に呼び ます。
自分のカスタムプロパティに注意してください。キャッシュを実装するかは開発者に任 されています。
iterator()
を使う¶多くのオブジェクトを持っている時、 QuerySet
のキャッシング動作は大量のメモ
リ使用につながる可能性があります。この場合、
iterator()
が助けになるかもしれませ
ん。
具体例として:
必要なSQLを生成するのにこれらで不十分なら:
QuerySet.extra()
を使う¶移植性は比較的低いですが、より強力なメソッドが
extra()
です。これはクエリに対する明
示的な SQL の追加を可能にします。これでもまだ十分強力じゃないなら:
データを検索したりモデルを作成するためのカスタム SQL
を自分で書いてください。 django.db.connection.queries
を使うと、 Django が
書き出したクエリを見ることができますので、そこから始めましょう。
一般的に、一つのデータ「集合」の別々の部分のために、データベースを複数回叩くの は、一つのクエリで一度に検索するよりも効率が低いです。 もしクエリがループの中で実行されていて、それ故に多くのデータベースクエリを一つ だけにまとめることが必要なら、特に重要です。
QuerySet.values()
と values_list()
を使う¶値の dict
か list
が必要なだけで、 ORM モデルオブジェクトが必要ないな
ら、 values()
を適切に使ってください。
これらはテンプレートコードのモデルオブジェクトを置き換えるのに便利です。辞書が
テンプレートで使われているものと同じ属性を持っているなら、うまく行きます。
QuerySet.defer()
と only()
を使う¶必要のない (またはほとんどの場合に必要ない) データベースカラムがあると分かって
いるなら、それらをロードしないように
defer()
と
only()
を使ってください。
もしそのようなカラムを 使う 場合は、 ORM が個別のクエリで取りに行かなければ
ならないことに注意してください。不適切にそれを使うなら、悲観的に考えましょう。
また、遅延フィールドを使ってモデルを構築する時に Django の中でいくらかの (小さ
な追加の) オーバーヘッドが発生することに注意してください。計測なしでの遅延
フィールドの使用に積極的になりすぎないでください。わずかなカラムしか使っていな
い時でも、データベースは結果の 1 つの行のためにほとんどの非 text 型、非
VARCHAR 型のデータをディスクから読み出さなければなりません。 defer()
と
only()
メソッドは多くのテキストデータや Python データに戻すために多くの処
理が必要となるフィールドのロードを避けるのにはとても便利です。例によって、まず
は計測、それから最適化をしましょう。
数を数えたいだけなら、 len(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 %}
これが最適な理由は:
with
を使うと、あとで使われるために user.email_all
を変数に保存
します。再利用できるキャッシュが使われます。{% if emails %}
行は QuerySet.__nonzero__()
の呼び出しを引き起こし
ます。これは user.emails.all()
がデータベースで実行されることにつながり
ます。少なくとも一行目が ORM オブジェクトに変換されます。結果がなければ
False を返し、あれば True を返します。{{ emails|length }}
は QuerySet.__len__()
を呼びます。他のクエリを
実行せずにキャッシュの残りを満たします。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)
ここで Bands
と Artists
は多対多の関係を持っているとします。
revision-up-to: | 11321 (1.1) |
---|
このセクションでは、 Django における HTTP リクエスト処理に関する情報を扱っ ています:
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 のコールバック関数 (自作のビュー) へのマッピングが入っているだ けです。
このマッピングは短くもできますし、必要なだけ長くもできます。他のマッピング も参照できます。また、 pure Python コードなので、動的に生成できます。
ユーザが Django で作られたサイト上のページをリクエストした時に、どの Python コードが実行されるかは以下のアルゴリズムで決定されます:
ROOT_URLCONF
に設定されています。ただし、
HttpRequest
オブジェクトに urlconf
という属性が設定されてい
た場合( request processing で設定されます)
、その値を ROOT_URLCONF
の代わりに使います。urlpatterns
とい
う名前の変数を探します。この変数の値は Python のリストで、
django.conf.urls.patterns()
が返すのと同じ形式です。HttpRequest
が、それ以降の引
数には正規表現でキャプチャした値が渡されます。エラーハンドリング
を見てください。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'),
)
注意:
^/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 はリクエストされた 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
の書き方¶urlpatterns
は django.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
(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
(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
(<module or pattern_list>)¶関数は現在の URLconf で “included” しておくべき Python のフル import パス を他の URLconf モジュールへと 取りこみます。
include()
も引数としてイテレート可能です。これは、
URL パターンを返します。
他の URLconf を取り込む を見てください。
Django が、リクエストされた URL にマッチする正規表現を 見つけられなかったとき、または例外が発生したとき、 Django は error-handling view を発生させます。ビューはルートの URlconf に設定された三つの変数で、指定されたケースで使用 されます。これらの変数の設定は、他のどの URLconf にも影響を 与えません。
エラービューのカスタマイズ を参照してください、もっと詳しいことが載っています。
handler403
¶呼ぶことができます、またはユーザがリソースにアクセスするのに 必要なパーミッション(permission 権限)を持っていない場合に、 ビュー関数への完全な Python の import パスが入った文字列です。
通常、これは 'django.views.defaults.permission_denied'
これのデフォルト値は満足させるでしょう。
403 (閲覧が禁止されています ビュー) にもっと沢山の情報が載っています。
handler403
は Django 1.4の新しいものです引数として取り出したテキストは、正規表現が何にマッチするかに関係なく通常の 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'),
)
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 の名前空間は二つのパートになります、両方とも文字列です:
admin
です。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),
)
どのスタイルを使うかは自由です。
このテクニックを使った場合 (文字列の代わりにオブジェクトを渡した場合) 上で説明した “”ビュープレフィクス”” は効果を及ぼさないので注意してください。
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 は :
演算子を使うことによって指定されます。
例えば、アドミンアプリケーションの、メインのインデックスページは
admin:index
を使うことによって参照されます。
名前空間は、ネスト(nest 入れ子)することができます。 foo:bar:whiz
と名前づけられた URL は、トップレベルの名前空間である foo
の中から
bar
という名前で定義されたものを探し、さらにその中から whiz
という名前のパターンを探します。
名前空間化された URL (すなわち、myapp:index
のようなもの) が与えられる
て解決すると、 Django は完全にパス化されたものをパーツごとに分解しようとしま
す。そしてその時に、 URL の探索を行おうとします。
最初に、 Django はアプリケーションの名前空間とマッチするものを探そうとし
ます。(この例では myapp
です。)これは、アプリケーションのインスタンスの
リストを作ります。
もし、 現在の アプリケーションが定義されていれば、 Django は
インスタンスの為に URL リゾルバを見つけて返します。 現在の アプリケーション
は、テンプレートコンテキストの上のアトリビュートとして指定されます。
アプリケーションは複数の配置を持つことを想定していて、テンプレートのレンダ
リングをするために使われる Context
か RequestContext
の
current_app
アトリビュートに指定されます。
現在のアプリケーションは reverse()
関数の引数から、手動で
指定することも出来ます。
もし、現在のアプリケーションがないとしたらどうでしょう。 Django は
デフォルトのアプリケーションインスタンスを探します。デフォルトの
アプリケーションインスタンスは、アプリケーションの名前空間とマッチする
インスタンスの名前空間を持つインスタンスです。(この例では、
myapp
のインスタンスは myapp
と呼ばれます)
もし、デフォルトのアプリケーションインスタンスがなかったとしたら、 Django は一番初めに配置されたアプリケーションのインスタンスを選択します。 それは、インスタンスの名前がどんなものであってもそうでしょう。
もし、提供された名前空間がステップの1のアプリケーションの名前空間と マッチしない場合、 Djangoはインスタンスの名前空間として、直接参照しようと します。
もし入れ子になった名前空間なら、ビューの名前が逆引きできるまで、名前空間の それぞれのパートに対してこれらのステップを繰り返します。
この行動の解決戦略をお見せしましょう、 myapp
: の foo
と呼ばれる
インスタンスと、 bar
と呼ばれるものについて考えてみましょう。
myapp
はメインのインデックスページを持っていて、 URL の名前は index で
す。セットアップを使って、参照可能にしてみましょう。:
bar
インスタンスの中でユーティリティページをレンダリングしならば、
myapp:index
は、 bar
インスタンスのインデックスページとして
逆引きします。myapp:index
は最後に登録された
myapp
のインスタンスを逆引きします。これらは通常のインスタンスではなく、
myapp
の最後のインスタンスが使われます。これは foo
か bar
で、プロジェクトの URL パターンに導入された順番に依存します。foo:index
は常に foo
インスタンスのインデックスページを解決しようと
します。もし、通常のインスタンスも存在すれば、すなわち、 myapp と名前づけられた インスタンスが存在すると、このようなことがおきます。:
bar
インスタンスの中でユーティリティページをレンダリングしならば、
myapp:index
は、 bar
インスタンスのインデックスページとして
逆引きします。myapp:index
は、通常のインスタンスの
インデックスページとして逆引きします。foo:index
は、再び foo
インスタンスのインデックスページへと
逆引きします。django.core.urlresolvers
ユーティリティ関数¶コード中で 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/'
args
と kwargs
は、 reverse()
を同時に通過することはできません。
ビューが全て正しいことを確認してください
URL 名の種々のパターンへのひも付けの生成の一部として、
reverse()
関数は、 URLconf ファイルのすべてと調べる各ビューの名前
をインポートする必要があります。これは各ビュー関数のインポートと関連
しています。もし 何か エラーが、ビュー関数のインポートする中で起きれば
、それは reverse()
がエラーを返す原因になります。もし、ビュー関数が
逆引きしようとしたものでなかったときでさえもです。
URLconf ファイルの中でビューの参照が実在するのと、 正しくインポートされていることを確かにしてください。 参照するビューがまだ書かれていないのに、それを行に含まないでください。 なぜならそのビューはまだインポートできないからです。
リリースノートを参照してください
reverse() の簡単に使えるバージョン
reverse_lazy
(viewname[, urlconf=None, args=None, kwargs=None, current_app=None])¶これは、プロジェクトの URLConf がロードされる前に、 URL 逆引きを使う必要がある 場合に便利です。いくつかの一般的なケースではこの関数がまさに必要になります:
url
アトリビュートとして、
URL を逆引きして提供する場合django.contrib.auth.decorators.permission_required()
の
デコレータのための login_url
引数のような)django.core.urlresolvers.resolve()
関数は、URL のパスとそれに付随してい
るビュー関数を呼び出すのに使われます。
resolve
(path, urlconf=None)¶path
はあなたがひも解きたい URL パスです。
reverse()
と一緒で、 urlconf
パラメータ
に関して考える必要はありません。この関数が返すのは ResolverMatch
オブジェクトで、これは解決された URL の様々なメタデータにアクセスできる
クラスです。
もし、 URL がひも解かれなかったら、関数は Http404
という例外を発生させます。
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
django.db.models.permalink()
は、モデルの get_absolute_url()
メソッ
ドのような、完全な URL パスを返す短いメソッドを書く際に便利なデコレータです。
詳細は、 django.db.models.permalink()
を参照してください。
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
に設定されています。お住まいの地域がここでなければ、
変更しておいてください。
このビュー関数は、現在の日付と時刻が入った 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 エラーよりはるかに良く使われるエラーなので、 もっと簡単に扱う方法を提供しています。
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
で、テンプ
レートツリーの一番上に配置せねばなりません。
Http404
例外を送出すると、 Django は 404 エラー処理用の特殊なビューをロー
ドします。デフォルトでは、このビューは
django.views.defaults.page_not_found
に設定されています。
django.views.defaults.page_not_found
は 404.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.html
という名前のテンプレートを
作成せねばなりません。django.template.RequestContext
なので、テンプレートには TEMPLATE_CONTEXT_PROCESSORS
で追加した変数も
渡ります。 (例えば MEDIA_URL
)DEBUG
を True
にすると、 404 ビューは
呼び出されず、代わりにトレースバックが表示されます。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.html
という名前のテンプレートを
作成せねばなりません。DEBUG
を True
にすると、 500 ビューは
呼び出されず、代わりにトレースバックが表示されます。リリースノートを参照してください
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 の様々な機能をビューに適用するためのいくつかのデコレータ が用意されています。
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)¶django.views.decorators.gzip
内のデコレータはビュー単位でコンテンツの
圧縮を制御します。
gzip_page
()¶このデコレータは、ブラウザが gzip 圧縮に対応している場合に圧縮します。
Vary
ヘッダをセットするので、
Accept-Encoding
ヘッダに基づいてキャッシュに保管されます。
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
(または ImageField
や FileField
のサブクラス) の名前です。従って、
上の例では、 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
を明示的にフォーム
のコンストラクタに渡さねばならないので注意してください。
これはフォームにファイルデータを結びつけるために必要な手順です。
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
FILE_UPLOAD_TEMP_DIR
FILE_UPLOAD_MAX_MEMORY_SIZE
より大きなファイルがアップロー
ドされたときにファイルデータを保存するディレクトリです。
デフォルト値はシステム標準の一時ファイルディレクトリです
(UNIX ライクのシステムでは /tmp
です)。
FILE_UPLOAD_PERMISSIONS
アップロードされたファイルに設定するファイルモードで、数字で表現
(例: 0644
) します。ファイルモードの意味は
os.chmod()
のドキュメントを参照してください。
この値を設定しないか、 None
にすると、アップロードされたファイ
ルのモードはオペレーティングシステムに依存します。ほとんどのプラッ
トフォームでは、一時ファイルのファイルモードは