もた日記

くだらないことを真面目にやる

Djangoメモ(11) : リクエスト処理の流れとURLconf (URL configuration)

Python 3.6.4 Django 2.0.2

A Complete Beginner's Guide to Djangoのチュートリアルを参考にリクエスト処理の流れとURLconf (URL configuration)について調べてみる。


詳細ページを表示

前回までで下図のBoard一覧を表示するページを作成したが、このページのBoard名(Django, Python, Random)をクリックしたら各BoardのTopic一覧を表示する詳細ページを作成することを考える。
Boardは複数個登録できるので、固定のURLではなくboards/1, boards/2といった特定パターンのURLでアクセスできるようにする必要がある。

f:id:wonder-wall:20180310231749p:plain


リクエスト処理の流れ

最初にドキュメントでDjangoのリクエスト処理の流れを調べてみる。
説明にあるURLconf (URL configuration) とはpure Pythonコードのモジュールで、URLパス表記とビューの Python 関数とのマッピングを記述(urls.pyurlpatterns)したものである。

  1. まず、Django は、どのモジュールをルート URLconf として使うか決定します。通常は、この値は ROOT_URLCONF に設定されています。ただし、 HttpRequest オブジェクトに urlconf という属性が設定されていた場合 ( middleware で設定されます) 、その値を ROOT_URLCONF の代わりに使います。

  2. Django はその Python モジュールをロードして、urlpatterns という名前の変数を探します。この変数の値は django.urls.path() または django.urls.re_path() インスタンスの Python リストでなければなりません。

  3. Django は URL パターンを順に調べて、リクエスト URL に最初にマッチしたところで止まります。

  4. ある 1 個の正規表現にマッチしたら、 Django はマッピングで指定されたビューを import して呼び出します。そのビューは単純な Python 関数 (もしくは class-based view) です。ビューには以下の引数が渡されます:

    • HttpRequest のインスタンス。
    • マッチした URL パターンから無名グループが返された場合は、正規表現でマッチした値が位置引数として渡されます。
    • URL パス表現でマッチした名前付き部分からはキーワード引数が作られます。ただし、 django.urls.path()django.urls.re_path() に kwargs オプション引数が指定されていた場合は、その中の引数で上書きされます。
  5. もし、 URL 正規表現が何にもマッチしなかったり、パターンマッチングプロセスの途中のどこかで例外が発生した場合、Django は適切なエラーハンドリングビューを呼び出します。下の Error handling を見てください。

上記リクエスト処理の流れを現状のプロジェクトで確認してみる。
プロジェクトのsettings.pyROOT_URLCONF = 'myproject.urls'と定義しているので下記myproject/urls.pyがルートURLconfとして使われる。
urlpatterns変数の値はdjango.urls.path()のリストになっているので問題はなし。

urlpatterns = [
    path('', views.home, name='home'),
    path('admin/', admin.site.urls),
]

ここでhttp://127.0.0.1:3000/のようなルートにアクセスするとpath('', views.home, name='home')にパターンがマッチするので、マッピングされたviews.homeが呼ばれることになる。
説明に「リクエスト URL に最初にマッチしたところで止まり」とあるようにマッチするパターンが複数ある場合は記述順が重要になる。


URLconf、ビュー、テンプレートの編集

ひとまず直接URLを入力して、詳細ページとしてパンくずリストを表示できるようにしてみる。
基本的にはチュートリアル通りだが、チュートリアルのDjangoバージョンは1.11でurl()関数を使用しているため、path()関数を使用するように変更する。

最初にmyproject/urls.pyboards/<int:pk>/の行を追加。
<int:pk>のような<, >で囲まれた部分はURLの一部がキャプチャされキーワード引数としてビュー関数に送信される。int部分は一致するパターンを識別するためのパスコンバータ(後述)で、pk部分はビュー関数で参照する変数名(pkはPrimary Keyの意味。変数名なので自由に決めて良い)になる。

from django.contrib import admin
from django.urls import path
from boards import views

urlpatterns = [
    path('', views.home, name='home'),
    path('boards/<int:pk>/', views.board_topics, name='board_topics'),
    path('admin/', admin.site.urls),
]

次にboards/views.pyboard_topicsを追加するがboard_topics(request, pk)pkurlpatternsで指定した変数名にする。
このpkには、例えばhttp://127.0.0.1:3000/boards/1/にアクセスすると1の部分がキャプチャされるので1がセットされる。
Board.objects.get(pk=pk)の最初のpkはPrimary Keyを意味し、Primary Keyで検索してオブジェクトを取得する。 モデルのPrimary Keyは明示的に定義していない場合は自動でidというフィールドがPrimary Keyとして作成されるので、Primary Keyを定義していないBoardモデルの場合はidと同じになる(つまりget(id=pk)でも動作する)。

def board_topics(request, pk):
    board = Board.objects.get(pk=pk)
    return render(request, 'topics.html', {'board': board})

最後にテンプレートファイルtemplates/topics.htmlを作成し、パンくずリストを表示する下記コードを記述する。

{% load static %}<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{ board.name }}</title>
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  </head>
  <body>
    <div class="container">
      <ol class="breadcrumb my-4">
        <li class="breadcrumb-item">Boards</li>
        <li class="breadcrumb-item active">{{ board.name }}</li>
      </ol>
    </div>
  </body>
</html>

これで編集は完了。
boards/1/にアクセスすると1番目に登録されているDjangoが表示され、

f:id:wonder-wall:20180311192053p:plain

boards/2/にアクセスすると2番目に登録されているPythonが表示される。

f:id:wonder-wall:20180311192105p:plain

intは数字にしかマッチしないので、例えばboards/a/にアクセスするとPage not foundになる。

f:id:wonder-wall:20180311192246p:plain


パスコンバータ

デフォルトではintの他に下記パスコンバータが使用できる。

  • str - 空でない文字列にマッチ。ただしパスのセパレータである/は除く
  • int - 0または正の整数にマッチ。返り値はint
  • slug- スラグとは英文字と数字、アンダースコア、ハイフンだけからなる短いラベル文字列。例えばbuilding-your-1st-django-siteにマッチ
  • uuid- UUIDフォーマットにマッチ。例えば075194d3-6885-417e-a8a8-6c931e272f00にマッチ。返り値はUUIDインスタンス
  • path - 空でない文字列にマッチ。ただしパスのセパレータである/は含む


まとめ

  • リクエスト処理の流れを確認
  • URLconfはURLパス表記とビュー関数とのマッピング設定
  • <int:pk>のような<, >で囲まれた部分はURLの一部がキャプチャされキーワード引数としてビュー関数に送信される
  • パスコンバータにはstr, int, slug, uuid, pathがある