MENU

Django Ninjaチュートリアル⑦(フォームデータ)

Django-Ninjaチュートリアル⑦(フォームデータ)

前回までの記事はこちらです。

前回までの記事が読まれている前提で書いていきますので、まだ読んでいない方はこちらから読み進めて追いついてください。

それでは始めていきましょう。

目次

フォームデータの取り扱い

今回は、Django Ninjaにおけるフォームデータの取り扱いについてみていきます。

公式ドキュメントはこちらです。

Django Ninjaでは、request.POSTにより送信されたデータ(application x-www-form-urlencodedまたはmultipart/form-data)の解析と検証ができます。

パラメータとしてのフォームデータ

まずは、フォームデータを扱う場合の基本的な形をみていきます。

from ninja import NinjaAPI, Path, Form # add
from example.Schema.api_v2_schema import PathDate, Item
from datetime import date

app_v2 = NinjaAPI(
  title = "Django Ninja Sample App2",
  version = "1.0.1"
)

〜略〜

# add 8
@app_v2.post("/login")
def login(request, username: str = Form(...), password: str = Form(...)):
  return {"username": username, "password": "*****"}

①まずは、Django NinjaからFormimportします。
②パラメータのデフォルト値としてimportしたFormを使用します。

これが、Django NinjaでFormデータを扱う場合の基本的な形です。

Schemaを使う場合

リクエストボディと同様に、Schemaによる型定義をFormデータを取り扱う際にも利用することができます。

リクエストボディの例を転用すると、

# add 9
@app_v2.post("/items")
def create_item(request, item: Item = Form(...)):
  return item

となります。

・関数パラメータとして、itemを設定し、それに対する型定義として別ファイルで定義したSchemaからItemを割り当てています。
・また、このitemのデフォルト値はFormにより設定しています。

これにより、自動生成ドキュメントを確認すると以下のようになっていると思います。

Formデータを設定した場合の自動生成ドキュメント

自動生成されたドキュメントでは、

POSTメソッドを使用していますが、APIからは値の入力ができません。
フロントエンド等の別のエンドポイントから送信されるデータを受け取るためです。
※なお、先ほどのloginの場合も同様のドキュメントが生成されていると思います。

以上のように、基本的なFormデータの使用方法により、外部からのデータを受け取ることができ、かつ、型定義を利用し、渡された値に対するバリデーションをかけることもできます。

リクエストフォーム+パスパラメータ+クエリパラメータ

リクエストボディと同様に、Formデータでも、パスパラメータとクエリパラメータを併用することができます。

Django Ninjaは、パスパラメータへ記載した引数は、関数パラメータの同一の引数に渡されます。
また、Form(...)で宣言された関数パラメーターはリクエストフォームのfieldsなどから取得します。

例を見てみます。

from ninja import NinjaAPI, Path, Form
from example.Schema.api_v2_schema import PathDate, Item
from datetime import date

app_v2 = NinjaAPI(
  title = "Django Ninja Sample App2",
  version = "1.0.1"
)

〜略〜

# add 10
@app_v2.post("/items/{item_id}")
def update(request, item_id: int, q: str, item: Item = Form(...)):
  return {"item_id": item_id, "item": item.dict(), "q": q}
  • item_idはパスパラメータとして渡されており、関数内のitem_idに渡されます。
  • qは、クエリパラメータとして使用されます。
  • itemは、Schemaで定義したItemを割り当てており、その値は、Formデータから受け取るように設定されています。

空のフォームデータの取り扱い

オプションのFormフィールドは、空の値が送信されることがよくあります。
この場合、送信されたデータは、空の文字列としてみなされ、int型やbool型のフィールドのバリデーションに失敗する場合があります。

これを回避するには、Pydanticのドキュメントで説明されているGeneric Classes as Typesを参考に対応します。

Schemaの設定

まずは、Schemaの設定を書き換えます。

import datetime
from ninja import Schema
from pydantic.fields import ModelField # add
from typing import Generic, TypeVar # add

PydanticField = TypeVar("PydanticField") # add
# TypeVar = Pythonのtypingクラスにある変数(静的型チェッカーとしての役割がある)

# add
class EmptyStrToDefault(Generic[PydanticField]):
  @classmethod
  def __get_validators__(cls): # __get_validators__は、入力データを解析して検証するバリデーターを取得する
    yield cls.validate
    
  @classmethod
  def validate(cls, value: PydanticField, field: ModelField) -> PydanticField:
    if value == "":
      return field.default
    return value

〜略〜

# add 2(change)
class Item(Schema):
  name: str
  description: str = None
  price: EmptyStrToDefault[float] = 0.0
  quantity: EmptyStrToDefault[int] = 0
  in_stock: EmptyStrToDefault[bool] = True
  

ベースはPydanticのGeneric Classes as Typesです。
Django Ninjaの公式ドキュメントは、Pydanticの公式ドキュメントを参考にしているようです。
処理の流れとしては、

  1. __get_validators__を用いたタイプパラメータに基づき、バリデーションを設定する
  2. そのために、EmptyStrToDefaultクラスを定義し、さらに内部で@classmethodを定義し、バリデーションの処理を設定する
  3. 1つ目の@classmethodはクラスに対するバリデーションをかけて、適切でない値の場合は、yield(停止)させています。
  4. 2つ目の@classmethodでは、valuefileldにそれぞれPydanticが提供しているFieldの型を割り当てている
  5. 受け取ったFormデータを定義したEmptyStrToDefault関数で検証し、空データで渡された場合、各fieldに設定していたデフォルト値を入力値とする

といった流れで処理していると思われます。

最後に

今回は、Django Ninjaにおけるフォームデータの取り扱いについてみてきました。

最後の空データの処理については、少し難しかったように感じました。

ですが、APIを構築するにあたっては避けられないところかと思いますし、フロントエンドを別のエンドポイントして、入力値を受け取る場合は、必須項目以外においては想定しておくべきところかと思いました。

今後のアプリケーション開発においてもしっかりと考えていくべきところかなと考えさせられました。

私もまだまだ勉強中ですので、間違っている箇所があった場合は、ご指摘いただけると幸いです。

今回はここまでです。また、次回もよろしくお願いします。

Django-Ninjaチュートリアル⑦(フォームデータ)

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

はじめまして、ふじです。
Python、Django、FastAPI、React.js、Next.jsを学習している、ずっと文系のプログラミング独学者です。

目次