| revision-up-to: | 11321 (1.1) unfinished |
|---|
すっきりした、簡潔で明解な 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コールバック関数 [, パラメタ辞書 [, エントリ名]])
パラメタ辞書 および エントリ名 はオプションで、省略できます (詳しくは「 ビュー関数に追加のオプションを渡す 」を参照してください)。
ノート
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 パス が入った文字列です。
include() also accepts as an argument an iterable that returns URL patterns.
後述の「 他の 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 に 送って続きの処理を行わせます。
Another possibility is to include additional URL patterns not by specifying the URLconf Python module defining them as the include argument but by using directly the pattern list as returned by patterns instead. For example:
from django.conf.urls.defaults import *
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)),
)
This approach can be seen in use when you deploy an instance of the Django Admin application. The Django Admin is deployed as instances of a AdminSite; each AdminSite instance has an attribute urls that returns the url patterns available to that instance. It is this attribute that you include() into your projects urlpatterns when you deploy the admin instance.
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" 変数が期待どおりに渡されま す。
When you need to deploy multiple instances of a single application, it can be helpful to be able to differentiate between instances. This is especially important when using named URL patterns, since multiple instances of a single application will share named URLs. Namespaces provide a way to tell these named URLs apart.
A URL namespace comes in two parts, both of which are strings:
URL Namespaces can be specified in two ways.
Firstly, you can provide the application and instance namespace as arguments to include() when you construct your URL patterns. For example,:
(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
This will include the URLs defined in apps.help.urls into the application namespace bar, with the instance namespace foo.
Secondly, you can include an object that contains embedded namespace data. If you include() a patterns object, that object will be added to the global namespace. However, you can also include() an object that contains a 3-tuple containing:
(<patterns object>, <application namespace>, <instance namespace>)
This will include the nominated URL patterns into the given application and instance namespace. For example, the urls attribute of Django's AdminSite object returns a 3-tuple that contains all the patterns in an admin site, plus the name of the admin instance, and the application namespace admin.
Once you have defined namespaced URLs, you can reverse them. For details on reversing namespaced urls, see the documentation on reversing namespaced URLs.
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"),
)
With these names in place (full-archive and arch-summary), you can target each pattern individually by using its name:
{% url arch-summary 1945 %}
{% url full-archive 2007 %}
つまり、二つの URL パターンがどちらも同じく archive ビューを参照してい ても、 url() に name パラメタを指定すれば、テンプレート中で二つを区 別できるというわけです。
URLconf エントリに付ける名前は、どんな文字を入れても構いません。 Python の 変数名規則には縛られません。
ノート
URLconf エントリに名前を付ける場合、他のアプリケーションの名前と衝突し なさそうな名前を使ってください。例えば、ある URL パターンを "comment" と命名していて、他のアプリケーションでも同じ名前を使っていたとしましょ う。この名前をテンプレートで使うと、もはやどちらの URL がテンプレートに 挿入されるのか判断する術はありません。
URLconf エントリの名前に、例えばアプリケーション名などからとったプレフィ クスを追加しておけば、名前が衝突しにくくなってよいでしょう。というわけ で、単に "comment" ではなく、 "myapp-comment" にするよう勧めます。
Namespaced URLs are specified using the : operator. For example, the main index page of the admin application is referenced using admin:index. This indicates a namespace of admin, and a named URL of index.
Namespaces can also be nested. The named URL foo:bar:whiz would look for a pattern named whiz in the namespace bar that is itself defined within the top-level namespace foo.
When given a namespaced URL (e.g. myapp:index) to resolve, Django splits the fully qualified name into parts, and then tries the following lookup:
First, Django looks for a matching application namespace (in this example, myapp). This will yield a list of instances of that application.
If there is a current application defined, Django finds and returns the URL resolver for that instance. The current application can be specified as an attribute on the template context - applications that expect to have multiple deployments should set the current_app attribute on any Context or RequestContext that is used to render a template.
The current application can also be specified manually as an argument to the reverse() function.
If there is no current application. Django looks for a default application instance. The default application instance is the instance that has an instance namespace matching the application namespace (in this example, an instance of the myapp called myapp).
If there is no default application instance, Django will pick the first deployed instance of the application, whatever its instance name may be.
If the provided namespace doesn't match an application namespace in step 1, Django will attempt a direct lookup of the namespace as an instance namespace.
If there are nested namespaces, these steps are repeated for each part of the namespace until only the view name is unresolved. The view name will then be resolved into a URL in the namespace that has been found.
To show this resolution strategy in action, consider an example of two instances of myapp: one called foo, and one called bar. myapp has a main index page with a URL named index. Using this setup, the following lookups are possible:
If there was also a default instance - i.e., an instance named myapp - the following would happen:
コード中で {% url %} のようなことをしたい場合のために、Django では django.core.urlresolvers.reverse() を提供しています:
viewname は関数名 (関数への参照か、関数名の文字列表記で、 urlpatterns 上で使っている形式) または URL パターン名 です。通常、 urlconf パラメタについて考える必要はなく、以下のように、固定引数とキー ワード引数だけを指定して url マッチングを行います:
from django.core.urlresolvers import reverse
def myview(request):
return HttpResponseRedirect(reverse('arch-summary', args=[1945]))
reverse() 関数は大抵の正規表現パターンを URL に逆変換できますが、全てと は限りません。今のところ、パターン中に垂直バー ("|") を含められません。 垂直バーを含むパターンは、 URL からビューへのマッチングには問題なく使えます が、パターンの逆変換はできないので注意してください。
The current_app argument allows you to provide a hint to the resolver indicating the application to which the currently executing view belongs. This current_app argument is used as a hint to resolve application namespaces into URLs on specific application instances, according to the namespaced URL resolution strategy.
Make sure your views are all correct
As part of working out which URL names map to which patterns, the reverse() function has to import all of your URLconf files and examine the name of each view. This involves importing each view function. If there are any errors whilst importing any of your view functions, it will cause reverse() to raise an error, even if that view function is not the one you are trying to reverse.
Make sure that any views you reference in your URLconf files exist and can be imported correctly. Do not include lines that reference views you haven't written yet, because those views will not be importable.
The django.core.urlresolvers.resolve() function can be used for resolving URL paths to the corresponding view functions. It has the following signature:
path is the URL path you want to resolve. As with reverse() above, you don't need to worry about the urlconf parameter. The function returns the triple (view function, arguments, keyword arguments).
For example, it can be used for testing if a view would raise a Http404 error before redirecting to it:
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)
# modify the request and response as required, e.g. change locale
# and set corresponding locale cookie
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() を参照してください。
Aug 19, 2010