クロスマート バックエンドエンジニアの武(タケ)(@pouhiroshi)です。
前回は5分で作れる!FastAPI入門編 ということで、簡単なFastAPIのご紹介をしました。
今回はWebフレームワークには欠かせない「入出力定義とValidation」をFastAPIではどのようにやるかをご紹介します。
FastAPIでこれらを実装する際に推奨されているライブラリが Pydantic です。
https://pydantic-docs.helpmanual.io/
Pydanticの読み方ですが、パッと検索した感じ見当たらなかったので勝手に「パイダンティック」と読んでいます。
もし正解をご存知の方がいらっしゃいましたら、ぜひ弊社のカジュアル面談にお越しいただき、教えてください・・・
使い方ですが、JavaのBeanValidatonに近い感じです。
フィールドに型ヒントをつければそれに沿ってバリデーションなどをおこなってくれます。
インストール方法
pip install pydantic
基本的な使い方
まず、人間(Person)データをidで1件抽出するエンドポイントを作ってみたいと思います。
出力データの定義はPersonクラスに定義し、APIにてインスタンスを作成して返そうと思います。
response_modelに返すpydanticのクラス型を指定できます。Personインスタンスを返すように定義します。
from fastapi import FastAPI from person import Person app = FastAPI() @app.get("/person/{person_id:int}", response_model=Person) def get_person(person_id: int): """ idによる人間データ取得 """ return Person( id=person_id, name="たけじい", job="会社員")
PersonクラスはPydanticのBaseModelを継承して作成します。
Field定義はなくても動作しますが、後ほどバリデーションの定義などに使用します。
Fieldのtitleに説明を書くと、swaggerに出力されますので便利です。
from pydantic import BaseModel, Field class Person(BaseModel): """ 人間クラス """ id: int = Field(title="id") """id""" name: str = Field(title="名前") """名前""" job: str | None = Field(title="職業") """職業"""
こちらでFastAPIを実行します。
swaggerのAPIレスポンス内 Schemaの欄に先ほど定義したtitleが表示されていますね。わかりやすくなりました。
実際にperson_idに値をいれて実行してみましょう。
Personインスタンスのデータがjsonで取得できましたね。idはintで定義しているので、ちゃんと数字型で表現されているようです。
出力に関してはこのような流れです。
Pydanticでフォームバリデーションを行う
次に、Validationについてやっていきたいと思います。
Personデータを登録するエンドポイントを例にしてやってみたいと思います。
idはAPI側で自動発番するものとして、入力としては要求しません。
なので、idフィールドは定義していません。
nameを必須とし、jobは無いことがありえるので任意入力とします。(str | None にあたるOptional表記です)
データ登録用のPersonクラスをPersonCreateクラスとして定義します。
from pydantic import BaseModel, Field class PersonCreate(BaseModel): """ 人間作成クラス """ name: str = Field(title="名前") """名前""" job: str | None = Field(title="職業") """職業"""
登録用エンドポイントは以下のようにします。
引数にPOSTされるPersonCreateのインスタンスを受け取ります。
本来はデータベースに登録して、idはデータベースに発番させますが、仮で999番をいれています。
発番したidをつかって先ほどGET /person/{person_id} で使っていたPersonインスタンスの形で返すようにしています。
@app.post("/person", response_model=Person) def create_person(person_create: PersonCreate): return Person( id=999, name=person_create.name, job=person_create.job)
swaggerを見てみましょう。
POST /person が作成されました。Request bodyの Schemaを見ると、赤の*がnameについていて、jobにはついていません。
ちゃんと定義した通り、nameが必須、jobが任意のフィールドになりました。
では実際に動かしてみましょう。
まずはサッカー選手のたけじいを登録してみます。
pycharmのデバッガーで、nameとjobが渡ってきているのを確認できました!
swaggerのレスポンスもちゃんとidが999で出力されましたね!
では、バリデーションの確認をしたいと思います。
nameが必須なので、nameをnullで登録してみます。
実行結果は以下のようになります。
http statusは422 で、response bodyにエラー内容が出力されます。
{ "detail": [ { "loc": [ "body", "name" ], "msg": "none is not an allowed value", "type": "type_error.none.not_allowed" } ] }
必須だけではなく、文字列長の制限もいれてみましょう。jobのほうに最大文字長10を設定してみましょう。
class PersonCreate(BaseModel): """ 人間作成クラス """ name: str = Field(title="名前") """名前""" job: str | None = Field(title="職業", max_length=10) """職業"""
Field属性のmax_lengthに最大文字数を定義するだけです。
swaggerを見てみましょう。
maxLength:10 が表示されていますね。
実際に11文字いれてみましょう。
12345678901と11文字いれたら、maxLengthのエラーになりました!!
複雑なvalidationを行うときは
@validator(プロパティ名) をつけて実装します。
ためしに名前が「たけじい」の場合はエラーにしてしまいましょう。値はvalue引数で受け取れます。
エラーにするにはValueError(エラーメッセージ)をraiseし、エラーがなければvalueをそのままreturnします。
from pydantic import BaseModel, Field, validator class PersonCreate(BaseModel): """ 人間作成クラス """ name: str = Field(title="名前") """名前""" job: str | None = Field(title="職業", max_length=10) """職業""" @validator("name") def validate_name(cls, value): if value == "たけじい": raise ValueError("たけじいは入力できません") return value
これで試してみます。
ちゃんとバリデーションエラーになりました!(ちょっと悲しい・・・・)
以上、FastAPIとPydanticを連携して、入出力関連の定義やバリデーションをスマートに実装できることが
実感できたと思います。
今後は、FastAPIのデータベース連携(SQLAlchemy) について記事を書きたいと思っています。
最後に
クロスマートでは絶賛エンジニア(バックエンド・フロントエンド)募集中です。
このお話を読んでちょっとでも気になった方がいたら
「話を聞きに行きたい」を押してみてください!