パスパラメータ#

これはDjango Ninjaシリーズチュートリアルの第10回です。
前回の記事では、Django NinjaがHTTPリクエストをどのように処理するかを紹介し、Pythonの型ヒントとの密接な結びつきを強調しました。
本記事では、Django Ninjaにおけるパスパラメータ(path parameters)の応用と詳細について探求します。これはHTTPリクエストを処理する際に非常によく見られるもので、特にRESTful APIにおいて顕著です。
本記事によるサンプルプロジェクトのコード変更は、すべてこちらのPRに集約されています。
GitHubサンプルプロジェクト#
一、パスパラメータとは?#
Path parametersはURLを構成する一部であり、URLパス(path)内の特定の位置にあり、異なる値(パラメータ)に基づいて渡される内容を決定し、リソースを動的に指定するために使用されます。
まずは、URL全体におけるpathの位置を理解しておきましょう。(画像はWikipediaより)

図から分かるように、パス(path)はURLの一部であり、しかも不可欠な部分です。
ただし注意が必要なのは、path parametersはDjangoやDjango Ninjaといったフレームワークが提供する一種の「機能」に過ぎないということです。URL自体にとって、pathはpathであり、単なる文字列にすぎません。
パスパラメータの例#
簡単な例を挙げて、path parametersの概念をよりよく理解しましょう。
実際のリクエストにおいて、123はパスパラメータを通じて、特定の記事のidを表します。
想像できる通り、もし456や789であれば、異なる結果が得られるでしょう。
これにより、APIは柔軟性を持ち、異なるリソースに対して操作を行うことができます。リソースごとに異なるルーティングを作成する必要はありません。それらのエンドポイントとルーティングはすべて同じであり、単に「パラメータ」が異なるだけなのです。
二、サンプルプロジェクトの変更#
次に、サンプルプロジェクトのコードを通じて、解説しながら本記事の内容を実演していきましょう。
ただし、その前に2つの変更を行う必要があります。
変更一:1次ルーティングプレフィックスのキャンセル#
1次ルーティングのプレフィックス/posts/と/user/をキャンセルし、view関数のrouterデコレータのパスをより完全で読みやすくします。
元々はこうでした。
現在はこうなっています。
注意していただきたいのは、これはチュートリアルの体験を向上させるためであり、実際の業務では通常このようなことはしません。そうしないと、モジュール化ルーティングのメリットが失われてしまうからです。
変更二:APIの追加#
path parametersを実演するために、この機能を実践するAPIが必要です。
「単一の記事情報を取得する」APIを新しく追加します。
よし、これで準備が整いました。path parametersについて学び始めましょう。
以下のコードはすべてサンプルプロジェクトから取られたものです。
三、Django Ninjaにおけるパスパラメータの使用#
Django Ninjaにおいて、パスパラメータの定義は非常に簡単です。routerデコレータと型ヒントを使用することで、これらのパラメータを簡単に処理し、自動的に型変換を行うことができます。
「パスパラメータを持つ」APIの定義#
Django Ninjaでパスパラメータを持つAPIをどのように定義するか見てみましょう。
@router.get(path='/posts/{post_id}/')
def get_post(request: HttpRequest, post_id: int) -> Post:
post = Post.objects.get(id=post_id)
return post
例において、{post_id}はパスパラメータであり、path文字列全体が解析(parsing)されて、get_post関数内のpost_id引数に渡されます。
Django Ninjaは、関数シグネチャで定義された型に従って、自動的に型変換を行います。
例えば、view関数でpost_id: intとマークした場合、Django Ninjaは自動的にURLからの文字列パラメータをintに変換します。
言い換えれば、path parametersを処理するフローは、同時に2つの効果を持っています。
- パラメータの型検証:フロントエンドから送られてきた
post_idの内容が誤った型であるにもかかわらず、view関数の内部でそのままその値の処理を試み続け、最終的にエラーが発生するのを防ぎます。 - view関数内部での自動型変換:関数内で手動で型変換を行う手間を省きます。
四、DjangoネイティブのPath Convertersとの互換性#
DjangoはURLを処理する際、元々リクエストパスを「厳格に照合」するための「path converters」を提供しています。
照合に成功して初めて、HTTPリクエストは特定のview関数に「転送」されます。
ここでの「厳格」とは、型がpath converterで定義されたものと一致して初めて、照合に成功するということを意味します。
よく使われるconvertersの型にはstr、int、slugなどがあり、これらはURL内のパラメータのフォーマットを制限することができます。
<int:post_id>は1つのpath converterであり、post_idが必ず整数であることを要求します。
強調しておきたいのは、path convertersの主な目的は型変換のためではないということです。それは単なる付随的なものです。本当の目的は、エンドポイントパスの「パターンマッチング」(pattern matching)を行うことにあります。
パターンが一致しない場合、そもそも照合に成功せず、当然型変換も行われません。
Django NinjaにおけるPath Converters#
Django Ninjaでは、これらのネイティブなpath convertersは依然として使用可能であり、さらに簡素化されています。
直接routerデコレータのパス文字列の中に書くだけです。
前述の通り、path converterがある場合、もしpost_idが有効なintでないなら、URLパターンマッチングは即座に失敗します。リクエストはview関数に入らず、型変換も行われません。
もし他に照合に成功するパスがない場合、Djangoは直接「404 Not Found」を返します。

個人的な意見としては、Django Ninjaにおいて、path convertersの機能はすでに部分的に型ヒントに取って代わられています。もしpath convertersを同時に使用したい場合は、両者の判定順序(path convertersが先に判定する)と、両者で設定する型が必ず同じでなければならない点に留意する必要があります。
五、リクエストの基本的なエラー処理#
リクエストのパスパラメータが型ヒントで定義された型と一致しない場合、Django Ninjaは自動的にエラーメッセージとヒント内容を含むHTTPレスポンス(ステータスコードは422)を返します。
例えば、ユーザーがリクエストしたパスが/posts/abc/(post_idパラメータに数字が与えられていない)だった場合、以下のレスポンスが得られます。
// http://127.0.0.1:8000/posts/abc/
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"post_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer"
}
]
}
このような自動エラー処理メカニズムは、APIの安定性を高めるだけでなく、開発者のエラー処理ロジックを簡素化します。
組み込みの422レスポンスはDjango Ninjaにおいて非常によく見られ、私たちの多くの時間を節約してくれます。
まとめと次へのステップ#
パスパラメータはRESTful APIにおける重要な構成要素であり、Django Ninjaは型ヒントと自動化されたエラー処理を通じて、パス内の動的パラメータを簡単に処理できるようにしてくれます。
さらに、それはDjangoネイティブのpath convertersと良好な互換性を保っており、効率的で簡潔な開発体験を提供します。
次回はクエリパラメータ(query parameters)について深く掘り下げ、Django Ninjaでこれらのパラメータをどのように処理し、APIの柔軟性と機能をさらに向上させるかについて説明します。