3.

Django の URL ディスパッチャー徹底解説(urls.py / path / re_path / include / name)

編集
この記事の要点
  • urls.pyurlpatterns リストで URL と view の対応を定義する
  • path("books//", views.detail) でパスコンバータ(int / str / slug / uuid)を指定
  • 正規表現が必要なら re_path(r"^books/(?P\d+)/$", ...)
  • name="book_detail" を付けると reverse("book_detail"){% url "book_detail" %} で URL 逆引き可能
  • include() でアプリごとの urls.py を分割し、app_name = "books" で名前空間化
  • 名前空間ありの逆引きは {% url "books:detail" id=1 %}

最小構成

# myproject/urls.py
from django.contrib import admin
from django.urls import path
from myapp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name='home'),                # /
    path('about/', views.about, name='about'),          # /about/
    path('books/', views.book_list, name='book_list'),  # /books/
    path('books//', views.book_detail, name='book_detail'),  # /books/123/
]
# myapp/views.py
from django.shortcuts import render, get_object_or_404
from .models import Book

def index(request):
    return render(request, 'index.html')

def book_list(request):
    return render(request, 'book_list.html', {'books': Book.objects.all()})

def book_detail(request, id):
    book = get_object_or_404(Book, pk=id)
    return render(request, 'book_detail.html', {'book': book})

パスコンバータ

コンバータマッチview に渡る型
str(default)/ 以外の文字列str
int0 以上の整数int
slug英数字・ハイフン・アンダースコアstr
uuidUUID 形式UUID
path/ を含む文字列全部str

path() と re_path() の使い分け

from django.urls import path, re_path
from . import views

urlpatterns = [
    # path: シンプル・読みやすい(推奨)
    path('articles///', views.month_archive),

    # re_path: 正規表現が必要な場合のみ
    re_path(r'^articles/(?P[0-9]{4})/(?P[0-9]{2})/$', views.month_archive),

    # 例: 4桁固定の年・2桁固定の月
    re_path(r'^reports/(?P\d{4})/(?P\d{2})/$', views.report),
]

path が使えるなら常に path を選ぶ。re_path は正規表現の細かい制限が必要なときだけ。

include() でアプリ毎に分割

プロジェクトが大きくなったら、アプリ単位で urls.py を分けます:

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('home.urls')),
    path('books/', include('books.urls')),
    path('users/', include('users.urls')),
    path('api/v1/', include('api.urls')),
]
# books/urls.py
from django.urls import path
from . import views

app_name = 'books'   # 名前空間

urlpatterns = [
    path('', views.book_list, name='list'),
    path('/', views.book_detail, name='detail'),
    path('/edit/', views.book_edit, name='edit'),
    path('new/', views.book_new, name='new'),
]

name による URL 逆引き

URL を文字列でハードコードせず、name で参照することでURL 変更時の影響を最小化できます:

# views.py
from django.shortcuts import redirect
from django.urls import reverse

def book_create(request):
    if request.method == 'POST':
        book = Book.objects.create(...)
        # ハードコード: ✗
        # return redirect('/books/' + str(book.id) + '/')

        # 名前空間 + name: ○
        return redirect('books:detail', id=book.id)

        # reverse でも書ける
        return redirect(reverse('books:detail', kwargs={'id': book.id}))
{# テンプレートから #}
{{ book.title }}
編集
新規作成
ホーム

名前空間(namespace)

app_name による名前空間で、複数アプリの URL 名衝突を防げます:

# books/urls.py
app_name = 'books'
urlpatterns = [path('', views.list, name='list')]

# users/urls.py
app_name = 'users'
urlpatterns = [path('', views.list, name='list')]

両方とも name='list' ですが、テンプレートでは {% url 'books:list' %} / {% url 'users:list' %} と区別できます。

クエリパラメータと URL 引数

# /search/?q=python&page=2 の場合

# URL ルーティング: クエリ部分は urls.py に書かない
urlpatterns = [
    path('search/', views.search, name='search'),
]

# view 側で request.GET から取得
def search(request):
    query = request.GET.get('q', '')
    page = int(request.GET.get('page', 1))
    ...

クラスベースビューと URLconf

# views.py
from django.views.generic import ListView, DetailView
from .models import Book

class BookListView(ListView):
    model = Book
    template_name = 'books/list.html'

class BookDetailView(DetailView):
    model = Book
    template_name = 'books/detail.html'
    pk_url_kwarg = 'id'

# books/urls.py
from .views import BookListView, BookDetailView

urlpatterns = [
    path('', BookListView.as_view(), name='list'),
    path('/', BookDetailView.as_view(), name='detail'),
]

カスタムコンバータ

独自のパス変換ロジックを書きたい場合:

# converters.py
class YearMonthConverter:
    regex = r'\d{4}-\d{2}'

    def to_python(self, value):
        year, month = value.split('-')
        return {'year': int(year), 'month': int(month)}

    def to_url(self, value):
        return f"{value['year']}-{value['month']:02d}"

# urls.py
from django.urls import path, register_converter
from . import converters, views

register_converter(converters.YearMonthConverter, 'ym')

urlpatterns = [
    path('archive//', views.archive),
    # /archive/2026-05/ にマッチ
]

URL 解決順

Django は urlpatterns上から順にマッチを試みて、最初にマッチしたものを使います:

# 順番が大事!
urlpatterns = [
    path('books/new/', views.book_new),         # ← /books/new/ はこっち
    path('books//', views.book_detail),  # 後ろなので /books/new/ には食われない
]

# 逆だと: /books/new/ →  はマッチしないので OK だが、
# /books/123/ より具体的なパスは先に書く習慣を

FAQ

Q: 末尾スラッシュは付ける?付けない?
A: Django のデフォルトは付けるAPPEND_SLASH = True でスラッシュ無しアクセス時に自動でリダイレクト。

Q: 404 / 500 のカスタムページ
A: ルート urls.py に handler404 = 'myapp.views.custom_404' を定義。テンプレートは templates/404.html

Q: URL リストを動的に作りたい
A: 通常は不要。複数同じパターンを生成するなら for ループで urlpatterns.extend([...])

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. クラスベースビュー(主流)の作り方とviewの分割
  2. 関数ベースビューの作り方とviewの分割
  3. URLディスパッチャー(ルーティング処理)
  4. GETとPOSTパラメータ受け取り
  5. クラスベースビューでGET/POSTリクエストの受け取り方
  6. クラスベースビューでテンプレートに値を渡す方法
  7. ビューでリダイレクト
  8. cookieの値の設定と取得
  9. HTTPステータスコードの返し方