スキルアップ

Djangoのurls.pyでのURLルーティング制御の仕組み

はじめに

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は、上から下へ定義順にパターンを評価します。

そして、最初に一致したもので処理を確定します。

  1. ブラウザが /users/signup/ を要求
  2. mysite/urls.pyurlpatterns
    上からチェック
  3. path('users/', include('users.urls'))
    に一致
    users.urlsバトン渡し
  4. users/urls.py
    path('signup/', views.signup_view, ...) に一致
  5. 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制御についての基本を
押さえるところから始めましょう。

  • この記事を書いた人

ニコ

元美容師、未経験からIT業界へ転職。
現在はIT企業でWEBシステム、
汎用業務システムのSEとして在籍!
趣味の筋トレ、スキルアップや副業など
社会人の総合量を磨く情報を発信。
170cm / 65kg / ライフタイムナチュラル

-スキルアップ