revision-up-to: | 8961 (1.0) |
---|
すっきりした、簡潔で明解な 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 コードが実行されるかは以下のアルゴリズムで決定されます:
URLconf の一例を示します:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^articles/2003/$', 'news.views.special_case_2003'),
(r'^articles/(\d{4})/$', 'news.views.year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
注意:
例のリクエストを挙げて説明しましょう:
上の例では、 名前なしの 簡単な正規表現グループを (丸括弧表記で) 使って URL から情報を取り出し、ビュー関数に 固定引数 として渡していました。より 高度な方法に、 名前つきの 正規表現グループで URL から情報を取り出し、ビュー の キーワード引数 に渡すものがあります。
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+)/$', 'news.views.article_detail'),
)
この URLconf は前の例と全く同じ機能を実現しますが、一つだけはっきり違うのは、 取り出した値を固定引数ではなくキーワード引数としてビュー関数に渡すという点 です。例えば:
実際には、このような名前つきグループを使うと URLconf が少しだけ明示的な書き 方になり、引数の順番に起因するバグを起こしにくくなります -- それに、自作ビュー の関数定義で引数順を変更できます。いうまでもなく、こうした恩恵は簡潔さの犠 牲の上に成り立っています: 開発者の中には、名前つきグループの構文が醜くて冗 長だと感じる人もいます。
URLconf パーザが正規表現中の名前つきグループと名前なしグループを扱う際のア ルゴリズムを以下に示します:
名前つきの引数があればそれを使います。名前なし引数は無視します。名前つきの 引数がなければ、すべての名前なし引数を固定引数として使います。
どちらの場合でも、追加のキーワード引数を指定できます。後述の「ビュー関数に 追加のオプションを渡す」を参照してください。
URLconf はリクエストされた URL を通常の Python 文字列として検索します。この 文字列には GET や POST のパラメタも、ドメイン名も入りません。
例えば、 http://www.example.com/myapp/ URLconf が検索対象にするのは myapp/ です。
http://www.example.com/myapp/?page=3 へのリクエストの場合でも myapp/ です。
URLconf はリクエストメソッドを調べません。換言すれば、同じ URL に対する POST, GET, HEAD といった全てのリクエストメソッドは同じビュー関 数に送られます。
urlpatterns は django.conf.urls.defaults.patterns() の返す形式の Python リストになります。 urlpatterns 変数の値を作るときには、常に patterns() を使うようにして下さい。
慣習的に、URLconf の先頭には from django.conf.urls.defaults import * を 書くことになっています。これによって、以下のようなオブジェクトにアクセスで きるようになります:
プレフィクス (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() 関数は、タプルの代わりとして 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 を文字列形式で指定する場合にしか使われません。
マッチする URL パターンがない場合に呼び出されるビュー関数への、完全な Python の import パスが入った文字列です。
デフォルト値は、 'django.views.defaults.page_not_found' です。普通はこ のデフォルト値で十分のはずです。
サーバエラーが生じた場合に呼び出されるビュー関数への、完全な Python の import パスが入った文字列です。サーバエラーはビュー関数のコード中で実行時エ ラーが生じたときに起きます。
デフォルト値は、 'django.views.defaults.page_not_found' です。普通はこ のデフォルト値で十分のはずです。
現在の URLconf に「取り込む」別の URLconf への完全な Python の import パス が入った文字列です。後述の「 他の URLconf を取り込む 」を参照してくださ い。
引数として取り出したテキストは、正規表現が何にマッチするかに関係なく通常の 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.defaults import *
urlpatterns = patterns('',
(r'^articles/(\d{4})/$', 'mysite.news.views.year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'mysite.news.views.month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'mysite.news.views.article_detail'),
)
この例では、各ビューには共通のプレフィクス、 'mysite.news.views' があり ます。 urlpatterns の各エントリに繰り返し入力する代わりに、 patterns() 関数の最初の引数にプレフィクスを指定して、各ビュー関数に適用 できます。
このことを念頭に置いて、上記の例をもっと簡潔に書くと以下のようになります:
from django.conf.urls.defaults import *
urlpatterns = patterns('mysite.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.defaults import *
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.defaults import *
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.defaults import *
urlpatterns = patterns('',
(r'^weblog/', include('django_website.apps.blog.urls.blog')),
(r'^documentation/', include('django_website.apps.docs.urls.docs')),
(r'^comments/', include('django.contrib.comments.urls')),
)
この例の正規表現が、 $ (行末にマッチする文字) を使っておらず、末尾にスラッ シュを付けていることに注意しましょう。 Django が include() をみつけると、 URL からマッチした部分を取り去り、残りの文字列を include された URLconf に 送って続きの処理を行わせます。
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" 変数が期待どおりに渡されま す。
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 された側の 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"),
)
アーカイブサマリのビューは、以下のようにしてテンプレートで参照できます:
{% url arch-summary 2007 %}
つまり、二つの URL パターンがどちらも同じく archive ビューを参照してい ても、 url() に name パラメタを指定すれば、テンプレート中で二つを区 別できるというわけです。
URLconf エントリに付ける名前は、どんな文字を入れても構いません。 Python の 変数名規則には縛られません。
Note
URLconf エントリに名前を付ける場合、他のアプリケーションの名前と衝突し なさそうな名前を使ってください。例えば、ある URL パターンを "comment" と命名していて、他のアプリケーションでも同じ名前を使っていたとしましょ う。この名前をテンプレートで使うと、もはやどちらの URL がテンプレートに 挿入されるのか判断する術はありません。
URLconf エントリの名前に、例えばアプリケーション名などからとったプレフィ クスを追加しておけば、名前が衝突しにくくなってよいでしょう。というわけ で、単に "comment" ではなく、 "myapp-comment" にするよう勧めます。
コード中で {% url %} のようなことをしたい場合のために、Django では django.core.urlresolvers.reverse() を提供しています。 reverse() 関 数のシグネチャは以下の通りです:
reverse(viewname, urlconf=None, args=None, kwargs=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 からビューへのマッチングには問題なく使えます が、パターンの逆変換はできないので注意してください。
django.db.models.permalink() は、モデルの get_absolute_url() メソッ ドのような、完全な URL パスを返す短いメソッドを書く際に便利なデコレータです。 詳細は、 django.db.models.permalink() を参照してください。
Aug 31, 2012