はじめに
Djangoを学び始めたばかりの方や、
Webアプリを作成している方がつまずきやすのが
ページにアクセスできないというエラーです。
- 画面を作ったのにブラウザで表示されない
- 次の画面へ遷移されない
- リンクをクリックしても404エラーになる
この原因の多くは、urls.py
に
正しくルーティングが設定されていないことにあります。
Djangoでは、ユーザーがアクセスするURLと、
処理を担当するView関数を紐づける必要があります。
その役割を担うのが urls.py です。
urls.pyは、Webアプリにとっていわば
住所録や案内マップの役割です。
ここで道筋を整えておかないと、
どんな画面を用意してもユーザーはたどり着けません。
この記事では、urls.pyの基本的な仕組みから具体的な書き方、
つまずきやすいポイントや応用的な活用方法までを、
私が学習した内容をもとにをまとめていきます。
この記事はこんな方におすすめです!
- Djangoで画面遷移がうまくいかずに困っている方
- urls.pyの書き方や分け方に自信がない方
- 初めて自分のWebアプリを作ろうとしている方
第1章:URLルーティングの役割
IT未経験からプログラミング言語の勉強をした後、
実際にDjangoのようなフレームワークを利用して
Webアプリを作る際、
最初にぶつかるものとして、URLルーティングがあります。
URLルーティングは、ユーザーがアクセスしたURLを
どの処理(View)に渡すか を決める交通整理係です。
ここを理解することで、Webアプリ全体での
画面遷移やページ構成の設計が一気にクリアになります。
1. ルーティングが必要な理由
Webブラウザを利用して展開されるWebアプリでは
/signup/
や /articles/42/
のような
パスでページを指定します。
Djangoは、受け取ったそのパスに対して、
どのPythonの処理を呼ぶかを urls.py
に
定義したルールに従って決めます。
もしルールが無ければ、
Djangoはどの処理を実行すべきか判断できず、
404(Not Found)を返します。
- ユーザー →
/login/
にアクセス - Django →
urls.py
に
「/login/ は login_view へ」
という対応があるかを探索 - 一致したらその View を呼び出し、
最終的にテンプレートを返す
ここでのキーワードは 、対応付け(マッピング)です。
urls.pyはDjangoのMTVモデルにおいて、
URLとViewを結びつけるのがルーティングの本質です。
2. プロジェクトの urls.py と アプリの urls.py の違い
Djangoでは、プロジェクト全体の入口を司る urls.py
と、
各アプリごとの入口を司る urls.py
を
分割して管理するのが基本です。
- プロジェクト側の
mysite/urls.py
- すべてのリクエストが最初に通過する
/users/
へ来たら users アプリへ/blog/
へ来たら blog アプリへ- アプリ単位での中継を行う
- アプリ側の
users/urls.py
など- users アプリ内の詳細なルート
/signup/
,/login/
などを
機能単位で定義- 各アプリの責任範囲が明確で保守しやすい
プログラミングの基本ですが、
小さく分けることで全体の見通しが良くなります。
これはDjangoが推奨するアプリ分割の思想でもあります。
それぞれのurls.pyのURL解決の流れでは、
Djangoは、上から下へ定義順にパターンを評価します。
そして、最初に一致したもので処理を確定します。
- ブラウザが
/users/signup/
を要求 mysite/urls.py
のurlpatterns
を
上からチェックpath('users/', include('users.urls'))
に一致
→users.urls
にバトン渡しusers/urls.py
でpath('signup/', views.signup_view, ...)
に一致signup_view
を呼び出し、レスポンスへ
3. パスコンバータで動的URLを受け取る
すべてのユーザーに一律の内容のページでよい場合は、/login/
のように静的URLで対応できます。
しかし、各ユーザのIDに対応させた/page/1/
や/page/2
などをすべてurls.pyに記載するのは
非現実的になってきます。
そこで利用するのが動的URLです。/page/
以降の1や2といった可変の部分を
パスコンバータで担います。
方法としては、urls.py内に記載するpath()
の
第1引数に動的部分を埋め込めます。
path('page/<int:id>/', views.detail, name='page_detail')
代表的なコンバータは以下です。
<int:id>
:
整数のみ一致(例:/page/42/
)<str:username>
:
スラッシュ以外の任意文字列<slug:slug>
:
英数・ハイフン・アンダースコア<uuid:uid>
:
UUID形式<path:subpath>
:
スラッシュを含む文字列(注意して使用)
view側で、このURL遷移で受け渡される動的な値を取得する場合、
パスコンバータで指定した同名の引数で受け取れます。
def detail(request, id):
...処理
4. nameの設定と逆引き
テンプレートやPython関数の中で画面遷移を記述する際に
URLを直書きすると、将来のパス変更で
リンク切れが発生する可能性があります。
リンク切れを起こすと、
想定しているはずの画面へ遷移できないなど
ユーザーにとって、不便を起こしてしまいます。
このようなURLの変更に柔軟に対応するためDjangoでは、
name 引数を付与して逆引きするのが定石です。
# urls.py
path('signup/', views.signup_view, name='signup')
# テンプレート(HTML)
<a href="{% url 'signup' %}">新規登録</a>
# Pythonコード
from django.urls import reverse
reverse('signup') # '/signup/' を返す
urls.pyでマッピング定義したURLに
紐づけたnameの値を利用することで
URLを変更しても、name が同じならリンクは壊れません。
これによって、保守性が段違いに向上します。
5. 末尾スラッシュと APPEND_SLASH の挙動
Djangoは既定で APPEND_SLASH=True
(settings.py)です。
例えば path('login/', ...)
だけを定義していて '/login'
にアクセスした場合、
自動で '/login/'
にリダイレクトします。
これは、CommonMiddleware
が担当しています。
第2章:つまずきやすいポイント
Djangoのurls.pyは仕組み自体はシンプルです。
しかし、Python未経験などの私のような初心者にとっては
ちょっとした記述誤りで、混乱を招きやすい部分でもあります。
ここでは、実際に私自身が学習中に
つまずいたポイントを整理していきます。
1. NoReverseMatchエラー
テンプレート内で {% url 'signup' %}
と書き、
指定の画面に遷移させたいのに
「NoReverseMatch」というエラーが出る。
これの原因としては、urls.py
で name='signup'
を付け忘れているか、
テンプレートとurls.pyの記述が一致していないことが原因です。
例として、nameの定義忘れの場合、
# urls.py
urlpatterns = [
path('signup/', views.signup_view),
]
<!-- signup.html -->
<a href="{% url 'signup' %}">サインアップ</a>
この場合、テンプレートであるHTMLで
DjangoのURL解決のために利用する「signup」という名前が
urls.pyに定義されえおらず、URLを探せずにエラーになります。
慣れてしまえば、忘れることはなくなってきますが、
基本としてurls.py側で 必ずnameを付与することで解消できます。
urlpatterns = [
path('signup/', views.signup_view, name='signup'),
]
2. includeを忘れる
Djangoは、プロジェクトのurls.py
と
アプリのurls.py
が分かれています。
しかし、Webアプリを作る際に
複数のURL定義用のファイルがあることに慣れていないと、
全体入り口となるプロジェクト側へのinclude()
が抜けやすいです。
includeしていない場合、
# mysite/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
]
この状態で users/urls.py
を作っても、
全体入り口となるプロジェクト側に読み込まれていない状態です。
そのため、各アプリ用のURLを指定しても
ページは404になります。
各アプリごとのURL定義をincludeさせしてしまえば、
この問題は解消されます。
正直、これについてもDjangoになれれば問題はないと思います。
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
]
3. Viewの紐づけ
URLのマッピング定義の基本は、
対象URL、どのViewにわたすか、対象URL用のname
を定義することです。
views.pyなどで、受け渡し先となる処理が記述していないと
URLのマッピングを定義していても動かない原因になります。
urls.pyだけではなく、紐づけ先となるviews.pyなど
他のPython処理も意識していくことが大切です。
第3章:実際のurls.pyの定義
ここからは、Djangoのurls.pyを実際に書いて定義するには
以下のポイントを押さえておけば大丈夫です。
- path関数
- name引数
- includeでルーティングを分割
- 動的URL(パスパラメータ)
1. path関数
urls.pyでのURLマッピングのためのエントリは、
ほとんどが path()
関数で定義されます。
from django.urls import path
from . import views
urlpatterns = [
path('hello/', views.hello_view),
]
この例では /hello/
にアクセスすると、hello_view
が呼び出されます。
path関数の引数は、3つあります。
- 第一引数:
URLパターン(例:'hello/'
) - 第二引数:
対応するView関数やクラス
(例:views.hello_view
) - 第三引数(任意):
name
(URLを逆引きするためのラベル)
第三引数のname
については、省力可ですが、
後述しますが、設定しておくことがおすすめです。
2. name引数
name
を設定することで、
テンプレートやPythonコードからURLを名前で呼び出せます。
# urls.py
urlpatterns = [
path('signup/', views.signup_view, name='signup'),
]
<!-- signup.html -->
<a href="{% url 'signup' %}">ユーザー登録はこちら</a>
このようにしておくことで、path関数の第一引数であるURLを
/register/
に変更しても、name='signup'
を変えなければ
リンクはそのまま有効です。
長期的な保守性を高めるためにも、
ルーティングにはnameを付けるのが基本になります。
3. includeでルーティングを分割
プロジェクトが大きくなるほど、
すべてのURLを1つのファイルに書くのは非効率です。
Djangoでは、アプリごとにurls.pyを分割し、
プロジェクト側のurls.pyで include()
を使って読み込みます。
プロジェクト側での読み込み方法は、以下です。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
]
そして、アプリ側では各画面ごとのURLマッピングを定義します。
こうすることで、機能ごとのアプリで
URLの保守性が高くなります。
from django.urls import path
from . import views
urlpatterns = [
path('signup/', views.signup_view, name='signup'),
path('login/', views.login_view, name='login'),
]
4. 動的URL(パスパラメータ)
ユーザーごとや記事ごとに異なるページを表示したい場合は、
URLにパラメータを埋め込むことで
動的にURLを扱うことができます。
記事詳細ページなど、テンプレートの骨組みが同じでも
埋め込む値が異なる場合などに利用します。
# urls.py
path('post/<int:post_id>/', views.post_detail, name='post_detail')
# views.py
def post_detail(request, post_id):
return HttpResponse(f"記事ID: {post_id} のページです")
/post/1/
にアクセスすると
「記事ID: 1 のページ」/post/99/
にアクセスすると
「記事ID: 99 のページ」
Djangoは自動的に <int:post_id>
を post_id
という引数としてビューに渡してくれます。
第4章:まとめ
今回は、Djangoのurls.pyについて
私が学習した基礎的な考えを中心にまとめました。
urls.pyの基本は「pathでURLをViewに結びつける」ことです。
URLの紐づけができるようになると、
Webアプリとしてだいぶ前進します。
そのため、urls.py
は
Webアプリ全体の設計図とも言える重要な存在です。
urls.pyの要点を押さえれば、
Djangoでの開発が一気にスムーズになります。
IT未経験の方や、Pythonでの副業を考えているなら
まずはこのURL制御についての基本を
押さえるところから始めましょう。