[FastaAPI][Pydantic] 入力・出力をキャメルケースにする

Pydanticで入出力をキャメルケースにする方法をまとめます。

サンプルコード


今回はFastAPI上でPydanticを利用するサンプルコードを使用します。動作を見たい場合は以下のライブラリをインストールしてください。

pip install fastapi uvicorn
 
# FastAPIサーバー実行
python -m uvicorn testmain:app --reload

以下は今回使うサンプルコードです。

TestBodyがリクエストボディ、TestResがレスポンスの型です。

前半は修正前の入出力がスネークケースのバージョン、後半は修正後のキャメルケースを出力するバージョンです。

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class TestRes(BaseModel):
    result_message: str
    status_code: int
    name: str


class TestBody(BaseModel):
    first_name: str
    last_name: str


@app.post("/test", response_model=TestRes)
def test(body: TestBody = Body()):
    return TestRes(
        result_message="テストメッセージ",
        status_code=200,
        name=body.first_name + body.last_name,
    )

###############################################

import stringcase
from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class TestRes(BaseModel):
    result_message: str
    status_code: int
    name: str

    class Config:
        allow_population_by_field_name = True
        alias_generator = stringcase.camelcase


class TestBody(BaseModel):
    first_name: str
    last_name: str

    class Config:
        alias_generator = stringcase.camelcase


@app.post("/test", response_model=TestRes)
def test(body: TestBody = Body()):
    return TestRes(
        result_message="テストメッセージ",
        status_code=200,
        name=body.first_name + body.last_name,
    )

入出力をキャメルケースにする


alias_generatorをConfigに設定することで、入出力のフィールド名を変更できます。

alias_generatorは文字列を引数に取り文字列を返す関数を指定できます。

https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator

まずはキャメルケースに変換するライブラリをインストール。

pip install stringcase

以下のようにBodyにalias_generatorにstringcase.camelcaseを設定すると、POSTリクエストのBody値はフィールド名と同じスネークケースではエラーが起き、キャメルケースのみ受け付けるようになります。

class TestBody(BaseModel):
    first_name: str
    last_name: str

    class Config:
        alias_generator = stringcase.camelcase

"""
以下の値を受け入れるようになる
{
    "firstName": "hoge",
    "lastName": "haaa"
}

これだとエラー
{
    "first_name": "hoge",
    "last_name": "haaa"
}
"""

同じようにResponseクラスに指定すると出力がキャメルケースになります。

ただ、alias_generatorを設定するとオブジェクト作成時の引数にも適用されてしまうためキャメルケースで指定する必要があります。

class TestRes(BaseModel):
    result_message: str
    status_code: int
    name: str

    class Config:
        alias_generator = stringcase.camelcase

@app.post("/test", response_model=TestRes)
def test(body: TestBody = Body()):
    # フィールド名はスネークケースだがキャメルケースで指定が必要になる
    return TestRes(
        resultMessage="テストメッセージ",
        statusCode=200,
        name=body.first_name + body.last_name,
    )

"""
レスポンスは以下のようになる
{
    "resultMessage": "テストメッセージ",
    "statusCode": 200,
    "name": "hogehaaa"
}
"""

フィールド名・alias両方を使えるようにする


先ほどの例ではTestResの引数もキャメルケースを指定しないといけないようになりました。

フィールド名はスネークケースなのにキャメルケースで指定が必要で不自然だったり、フィールド名と違うので静的解析で型のエラーが出たりしてしまいます。

allow_population_by_field_nameをTrueにすることでフィールド名のスネークケースも引数で使えるようになります。出力にはalias_generatorが使用されるのでキャメルケースのままです。

また、Bodyにallow_population_by_field_nameを指定すると、フィールド名のスネークケースでもalias_generatorのキャメルケースどちらでも受け入れるようになります。

class TestRes(BaseModel):
    result_message: str
    status_code: int
    name: str

    class Config:
        allow_population_by_field_name = True
        alias_generator = stringcase.camelcase

@app.post("/test", response_model=TestRes)
def test(body: TestBody = Body()):
    # フィールド名で指定ができるようになる
    return TestRes(
        result_message="テストメッセージ",
        status_code=200,
        name=body.first_name + body.last_name,
    )

"""
レスポンスは以下のようになる
{
    "resultMessage": "テストメッセージ",
    "statusCode": 200,
    "name": "hogehaaa"
}
"""

Bodyはキャメルケースのみ受け付けて、レスポンスは出力キャメルケースで引数はフィールド名(スネークケース)を指定できる最終的なコードは以下のようになります。

import stringcase
from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class TestRes(BaseModel):
    result_message: str
    status_code: int
    name: str

    class Config:
        allow_population_by_field_name = True
        alias_generator = stringcase.camelcase


class TestBody(BaseModel):
    first_name: str
    last_name: str

    class Config:
        alias_generator = stringcase.camelcase


@app.post("/test", response_model=TestRes)
def test(body: TestBody = Body()):
    return TestRes(
        result_message="テストメッセージ",
        status_code=200,
        name=body.first_name + body.last_name,
    )

コメントを残す

メールアドレスが公開されることはありません。