コンテンツにスキップ

クエリパラメータ#

2024 iThome鉄人レース

これはDjango Ninjaシリーズチュートリアルの第11回です。

前回は、リクエストURLにおけるパスパラメータの処理方法について議論しました。

本記事では、クエリパラメータ(query parameters)を紹介します。これはRESTful APIにおいて、フィルタリング条件などの追加情報を渡すために使用される重要な部分です。

Django Ninjaにおけるクエリパラメータの処理は非常に簡単かつ直感的で、様々な方法で実現できます。

本記事のすべてのコード変更は、こちらのPRを参照してください。

GitHubサンプルプロジェクト#

👉 Django-Ninja-Tutorial


一、クエリパラメータとは?#

クエリパラメータはURL内のオプションのパラメータであり、通常はpathの後ろに?key=valueの形式で現れ、追加情報を渡すために使用されます。

例えば、特定の著者の記事をフィルタリングしたい場合、URLのpathは次のように書かれるかもしれません。

/posts/?author=john

URLはクエリパラメータauthor=johnを渡し、Johnによって書かれた記事をフィルタリングしたいということを示しています。

二、サンプルプロジェクトの変更#

クエリパラメータをよりリアルに紹介するために、元の「すべての記事を取得する」APIを変更し、簡単な「フィルタリング」機能を追加する必要があります。

ちなみに、複雑なフィルタリング機能については、〈第23回:フィルタリング(Filtering)〉で紹介します。

変更後、リクエストにクエリパラメータが含まれている場合、APIはこれらのパラメータを通じてクエリ結果を制限することができるようになります。以下の通りです。

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = None):
    posts = Post.objects.all()
    if title:
        posts = posts.filter(title__icontains=title)  # フィルタリングロジックの実装
    return posts

ここでは「記事のタイトル」でフィルタリングを行っています。

小さなヒント:プロジェクトのAPIはまだ使用できません。一つにはdbにデータがないからであり、もう一つには関連するスキーマをまだ書いていないからです。現段階では読解の参考程度に留めてください。でも心配しないでください。すぐに動くようになりますよ☺️

よし、コードの変更が終わったので、次に解説を行います。


三、Django Ninjaにおけるクエリパラメータの使用#

Django Ninjaにおいて、クエリパラメータを処理する最も簡単な方法は、それらをview関数のオプション引数として直接扱うことです。引数のデフォルト値Noneを通じて

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = None):

この例では、title引数はオプションの文字列None | str = None)として定義されています。

  • URLにtitleクエリパラメータが含まれている場合、Django Ninjaは自動的にその値を引数として扱い、get_posts関数に渡します。
  • URLにこのクエリパラメータがない場合、関数は引数を受け取らず、その際に関数内のtitleの値はNoneになります。なぜなら、それにはデフォルト値があるからです。

この例について、さらに以下の点に注意する必要があります

  1. クエリパラメータはrouterデコレータのpath引数のパス内に書く必要はありません
  2. クエリパラメータには通常デフォルト値があります。具体的な値であれ、上記のNoneであれ。もしデフォルト値が欠けている場合、クエリパラメータが存在しないと、Django Ninjaは422レスポンスを返します。
  3. デフォルト値がNoneの場合、型ヒントの書き方に留意してください:None | str = None。(Optional[str] = Noneに相当します)
  4. クエリパラメータはパスパラメータと同様に、関数の型ヒントに従って型変換が行われます。もし型がマークされていない場合、両者のデフォルトの型はどちらもstrです。なぜなら、URLは本質的にすべて文字列だからです。

以上の書き方は簡単かつ直接的で、ほとんどの状況に適用できます。

しかし、クエリパラメータに対してより複雑な検証や制限を行う必要がある場合は、高度なテクニックであるQueryを使用する必要があります。


四、Queryオブジェクトの使用#

クエリパラメータの長さや範囲を制限したり、APIドキュメントに追加情報を付け加えたりするなど、より詳細な制御が必要な場合は、Queryを使用してクエリパラメータを設定・処理することができます。

正直に言うと、私は以前の開発で実はあまりQueryを使用したことがありませんでしたが、その最も重要な20%の特性を理解しておくことは、間違いなく非常に役立ちます。

Queryの紹介#

Queryオブジェクトを通じて、私たちはクエリパラメータをより細かく定義・検証することができます。

実際、Django Ninjaのソースコードを見たことがあれば気づくはずです。これは実は関数なのです。ただ、同じ名前のクラスオブジェクトを返すだけです。

解説の便宜上、私たちはこれを総称してQueryオブジェクトと呼びます。結局のところ、Pythonではすべてがオブジェクトですから。

この変更された例を参考にしてください。

from ninja import Query, Router
...

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = Query(None)):
    ...

これは元のこの書き方とほぼ等価です。(まだ微妙な違いはありますが、一旦無視して構いません)

def get_posts(request: HttpRequest, title: None | str = None):

追加のメリットもないのに、なぜわざわざ複雑な書き方へ変える必要があるのか、不思議に思うかもしれません。

それはもちろん、より複雑な書き方の方が、より多くのことができるからです。

クエリ文字列の長さを制限する#

例えば、titleというクエリ文字列を制限して、長すぎず短すぎないようにしたいとします。

長さは2から10文字の間である必要があると仮定しましょう。

その場合、次のように書くことができます。

def get_posts(
    request: HttpRequest,
    title: None | str = Query(None, min_length=2, max_length=10),
):

この例では、Queryを使用してtitleクエリパラメータを定義し、追加でmin_lengthmax_lengthという2つのQuery初期化パラメータ設定を与えました。

こうすることで、titleクエリパラメータの長さが2から10文字の間にあることを保証できます。

ユーザーが入力したtitleがこの長さの要求を満たさない場合、前の記事で述べたように、Django Ninjaは自動的にステータスコード422のレスポンスを返します。私たちが手動でこれらの検証ロジックや関連するレスポンスを処理する必要はありません。

// 422 Unprocessable Entity
{
    "detail": [
        {
            "type": "string_too_short",  // クエリパラメータが短すぎる
            "loc": [
                "title",
                "title"
            ],
            "msg": "String should have at least 2 characters",
            "ctx": {
                "min_length": 2
            }
        }
    ]
}

Queryのその他のよく使われるパラメータ#

min_lengthmax_lengthの他にも、Queryはクエリ条件を制限したり、APIドキュメントに追加情報を補足したりするための多くの実用的なパラメータを提供しています。よく使われるものは以下の通りです。

  • gtge:クエリパラメータの値が特定の数値より大きい、または以上でなければならない。
  • ltle:クエリパラメータの値が特定の数値より小さい、または以下でなければならない。
  • exampleexamples:APIドキュメント用にクエリパラメータのサンプル値を提供し、ユーザーにパラメータの使い方をより理解しやすくする。

この部分の実演は割愛します。


まとめと次へのステップ#

クエリパラメータはRESTful APIにおいてよく見られる重要な構成要素です。Django Ninjaでは、簡単な方法でクエリパラメータを処理することもできますし、Queryを使用してより高度な検証や制御を行うこともできます。

Django NinjaがURLの関連パラメータをどのように処理するかを理解した後は、いよいよメインイベントです。

次は、Django NinjaでHTTPのrequest bodyをどのように処理するかを探求し、スキーマを使用してデータ検証とデシリアライズを行う方法を紹介します。これにより、複雑なリクエスト情報を柔軟に処理できるようになります。