コンテンツへスキップ

レスポンスモデル - 戻り値の型

パス操作関数戻り値の型にアノテーションを付けることで、レスポンスに使用する型を宣言できます。

関数パラメータの入力データと同じように型アノテーションを使用でき、Pydanticモデル、リスト、辞書、整数、ブール値などのスカラー値を使用できます。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: list[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]
from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: List[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> List[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]

FastAPI はこの戻り値の型を次のように使用します。

  • 返されたデータを検証します。
    • データが無効な場合(例: フィールドが不足している場合)、それはあなたのアプリコードが壊れていて、あるべきものを返していないことを意味し、不正なデータを返す代わりにサーバーエラーを返します。これにより、あなたとあなたのクライアントは、期待されるデータとデータ形式を受け取ることができると確信できます。
  • OpenAPI のパス操作にレスポンス用のJSON Schemaを追加します。
    • これは自動ドキュメントで使用されます。
    • また、自動クライアントコード生成ツールでも使用されます。

しかし、最も重要なのは

  • 出力データを戻り値の型で定義されたものに制限およびフィルタリングします。
    • これはセキュリティにとって特に重要であり、これについては後述します。

response_model パラメータ

型が宣言しているものとまったく同じではないデータを返したい、または返す必要がある場合があります。

たとえば、辞書を返すか、データベースオブジェクトを返したいが、それをPydantic モデルとして宣言することができます。この方法では、Pydantic モデルが、返されたオブジェクト(例: 辞書またはデータベースオブジェクト)のすべてのデータドキュメント、検証などを実行します。

戻り値の型アノテーションを追加した場合、ツールやエディタは、関数が宣言したもの(例: Pydantic モデル)とは異なる型(例: dict)を返していることを示す(正しい)エラーで文句を言います。

そのような場合、戻り値の型の代わりにパス操作デコレータパラメータresponse_modelを使用できます。

response_model パラメータは、次のいずれかのパス操作で使用できます。

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • など
from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:
    return item


@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]
🤓 その他のバージョンとバリアント
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: list[str] = []


@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:
    return item


@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]
from typing import Any, List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: List[str] = []


@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:
    return item


@app.get("/items/", response_model=List[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]

Note

response_model は、「デコレータ」メソッド(getpostなど)のパラメータであり、すべてのパラメータやボディのように、パス操作関数のパラメータではないことに注意してください。

response_model は、Pydantic モデルのフィールドに宣言するのと同じ型を受け取ります。したがって、Pydantic モデルにすることもできますが、たとえば、List[Item] のような Pydantic モデルのlistにすることもできます。

FastAPI は、この response_model を使用して、すべてのデータドキュメント、検証などを行い、また出力データをその型宣言に変換およびフィルタリングします

ヒント

エディタ、mypy などで厳密な型チェックを行っている場合は、関数の戻り値の型を Any と宣言できます。

そうすることで、エディタに意図的に何でも返していることを伝えます。しかし、FastAPI は依然として response_model を使用してデータのドキュメント、検証、フィルタリングなどを行います。

response_model の優先順位

戻り値の型と response_model の両方を宣言した場合、response_model が優先され、FastAPI で使用されます。

この方法により、応答モデルとは異なる型を返す場合でも、関数に正しい型アノテーションを追加して、エディタや mypy のようなツールで使用できます。そして、FastAPI に response_model を使用してデータの検証、ドキュメントなどを行わせることができます。

また、response_model=None を使用して、そのパス操作のレスポンスモデルの作成を無効にすることもできます。Pydantic の有効なフィールドではないものに型アノテーションを追加する場合にそうする必要があるかもしれません。その例については、以下のセクションで説明します。

同じ入力データを返す

ここでは、プレーンテキストのパスワードを含む UserIn モデルを宣言しています。

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user

情報

EmailStr を使用するには、まず email-validator をインストールしてください。

仮想環境を作成し、アクティブ化してからインストールしてください。たとえば、

$ pip install email-validator

または

$ pip install "pydantic[email]"

そして、このモデルを入力の宣言と、同じモデルを出力の宣言に使用しています。

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user

これで、ブラウザがパスワード付きのユーザーを作成するたびに、API はレスポンスで同じパスワードを返します。

この場合、パスワードを送信しているのが同じユーザーであるため、問題にならないかもしれません。

しかし、同じモデルを別のパス操作に使用した場合、ユーザーのパスワードをすべてのクライアントに送信してしまう可能性があります。

危険

ユーザーのプレーンパスワードを保存したり、このようにレスポンスで送信したりしないでください。すべての注意点を知っていて、何をしているのかを理解している場合を除きます。

出力モデルを追加する

代わりに、プレーンテキストのパスワードを含む入力モデルと、パスワードを含まない出力モデルを作成できます。

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 その他のバージョンとバリアント
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

ここでは、パス操作関数がパスワードを含む同じ入力ユーザーを返しているにもかかわらず

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 その他のバージョンとバリアント
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

...パスワードを含まないモデル UserOutresponse_model として宣言しました。

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 その他のバージョンとバリアント
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

したがって、FastAPI は、出力モデルで宣言されていないすべてのデータをフィルタリングします(Pydantic を使用して)。

response_model または戻り値の型

この場合、2つのモデルが異なるため、関数の戻り値の型をUserOutとアノテーション付けすると、エディタやツールは、クラスが異なるため無効な型を返していると文句を言うでしょう。

そのため、この例ではresponse_modelパラメータで宣言する必要があります。

...しかし、それを乗り越える方法については、以下を読み続けてください。

戻り値の型とデータのフィルタリング

前の例の続きです。関数に1つの型でアノテーションを付けたいのですが、実際にはより多くのデータを含むものを関数から返したいと思っていました。

FastAPI には、レスポンスモデルを使用してデータをフィルタリングし続けてほしいと思っています。そうすれば、関数がより多くのデータを返したとしても、レスポンスにはレスポンスモデルで宣言されたフィールドのみが含まれることになります。

前の例では、クラスが異なっていたため、response_model パラメータを使用する必要がありました。しかし、それは同時に、関数の戻り値の型をチェックするエディタやツールのサポートが得られないことも意味します。

しかし、このようなことをする必要があるほとんどの場合、モデルは、この例のように、データをフィルタリング/削除するだけであることが望ましいです。

そして、そのような場合、クラスと継承を使用することで、関数型アノテーションを活用して、エディタやツールでより良いサポートを得ることができ、FastAPIのデータフィルタリングも引き続き利用できます。

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class BaseUser(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


class UserIn(BaseUser):
    password: str


@app.post("/user/")
async def create_user(user: UserIn) -> BaseUser:
    return user
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class BaseUser(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None


class UserIn(BaseUser):
    password: str


@app.post("/user/")
async def create_user(user: UserIn) -> BaseUser:
    return user

これにより、型に関しては正しいコードであるため、エディターや mypy などのツールサポートが得られるだけでなく、FastAPI からのデータフィルタリングも得られます。

これはどのように機能するのでしょうか?見ていきましょう。🤓

型アノテーションとツール

まず、エディタ、mypy、その他のツールがこれをどのように認識するかを見てみましょう。

BaseUser には基本フィールドがあります。次に UserInBaseUser を継承し、password フィールドを追加するため、両方のモデルのすべてのフィールドが含まれます。

関数の戻り値の型を BaseUser とアノテーション付けしていますが、実際には UserIn インスタンスを返しています。

エディタ、mypy、その他のツールはこれについて文句を言いません。なぜなら、型付けの観点から見ると、UserInBaseUser のサブクラスであり、これは BaseUser であることが期待される場合に有効な型であることを意味するからです。

FastAPI データフィルタリング

次に、FastAPI の場合、戻り値の型を確認し、返されるものに型で宣言されているフィールドのみが含まれていることを確認します。

FastAPI は Pydantic を内部でいくつか処理し、クラス継承の同じルールが返されるデータのフィルタリングに使用されないようにします。そうでなければ、期待以上に多くのデータを返してしまう可能性があります。

この方法で、ツールサポートデータフィルタリングの両方で型アノテーションという両方の良いとこ取りができます。

ドキュメントで確認する

自動生成されたドキュメントを見ると、入力モデルと出力モデルの両方が独自の JSON Schema を持っていることを確認できます。

そして、両方のモデルが対話型 API ドキュメントに使用されます。

その他の戻り値の型アノテーション

Pydantic の有効なフィールドではないものを返し、それを関数のアノテーションとして記述し、ツールのサポート(エディタ、mypy など)のみを得るケースがあるかもしれません。

レスポンスを直接返す

最も一般的なケースは、高度なドキュメントで後述するように、レスポンスを直接返すことでしょう。

from fastapi import FastAPI, Response
from fastapi.responses import JSONResponse, RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return JSONResponse(content={"message": "Here's your interdimensional portal."})

この単純なケースは、戻り値の型アノテーションが Response クラス(またはそのサブクラス)であるため、FastAPI によって自動的に処理されます。

そして、RedirectResponseJSONResponse の両方が Response のサブクラスであるため、型アノテーションは正しく、ツールも問題ありません。

レスポンスのサブクラスにアノテーションを付ける

型アノテーションで Response のサブクラスを使用することもできます。

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/teleport")
async def get_teleport() -> RedirectResponse:
    return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")

これは RedirectResponseResponse のサブクラスであるため、これも機能し、FastAPI はこの単純なケースを自動的に処理します。

無効な戻り値の型アノテーション

しかし、Pydantic の有効な型ではない他の任意のオブジェクト(例: データベースオブジェクト)を返し、それを関数でそのようにアノテーションすると、FastAPI はその型アノテーションから Pydantic レスポンスモデルを作成しようとして失敗します。

複数の型の間で、1つ以上の型がPydanticの有効な型ではないユニオンがある場合も同様で、例えばこれは失敗します 💥

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response | dict:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Union[Response, dict]:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}

...これは、型アノテーションがPydantic型ではなく、単一のResponseクラスまたはサブクラスではなく、Responsedictの間のユニオン(どちらか一方)であるため失敗します。

レスポンスモデルを無効にする

上記の例の続きですが、FastAPI によって実行されるデフォルトのデータ検証、ドキュメント、フィルタリングなどを必要としない場合があります。

しかし、エディタや型チェッカー(例: mypy)などのツールからのサポートを得るために、関数の戻り値の型アノテーションは残しておきたいかもしれません。

この場合、response_model=None を設定することで、レスポンスモデルの生成を無効にできます。

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Response | dict:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Union[Response, dict]:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}

これにより、FastAPI はレスポンスモデルの生成をスキップし、FastAPI アプリケーションに影響を与えることなく、必要な戻り値の型アノテーションを持つことができます。🤓

レスポンスモデルのエンコーディングパラメータ

レスポンスモデルには、次のようなデフォルト値があるかもしれません。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: List[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
  • description: Union[str, None] = None (Python 3.10 では str | None = None) のデフォルト値は None です。
  • tax: float = 10.5 のデフォルト値は 10.5 です。
  • tags: List[str] = [] のデフォルト値は空のリストです: []

ただし、実際に保存されていない場合は、結果から除外したい場合があります。

たとえば、NoSQL データベースに多くのオプション属性を持つモデルがあるが、デフォルト値でいっぱいの非常に長い JSON レスポンスを送信したくない場合などです。

response_model_exclude_unset パラメータを使用する

パス操作デコレータパラメータ response_model_exclude_unset=True を設定できます。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: List[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]

そうすると、それらのデフォルト値はレスポンスに含まれず、実際に設定された値のみが含まれます。

したがって、ID foo のアイテムに対してそのパス操作にリクエストを送信すると、レスポンス (デフォルト値を含まない) は次のようになります。

{
    "name": "Foo",
    "price": 50.2
}

情報

Pydantic v1 ではメソッドは .dict() と呼ばれていましたが、Pydantic v2 では非推奨 (ただし引き続きサポートされています) となり、.model_dump() に改名されました。

ここでの例は Pydantic v1 との互換性のために .dict() を使用していますが、Pydantic v2 を使用できる場合は代わりに .model_dump() を使用してください。

情報

FastAPI は Pydantic モデルの .dict()exclude_unset パラメータとともに使用して、これを実現しています。

情報

次のようにも使用できます。

  • response_model_exclude_defaults=True
  • response_model_exclude_none=True

Pydantic のドキュメントexclude_defaults および exclude_none に記載されています。

デフォルト値を持つフィールドのデータ

しかし、データがデフォルト値を持つモデルのフィールドに値を持っている場合、ID bar のアイテムのように

{
    "name": "Bar",
    "description": "The bartenders",
    "price": 62,
    "tax": 20.2
}

それらはレスポンスに含まれます。

デフォルト値と同じ値のデータ

データがID bazのアイテムのようにデフォルト値と同じ値を持っている場合

{
    "name": "Baz",
    "description": None,
    "price": 50.2,
    "tax": 10.5,
    "tags": []
}

FastAPIは十分賢い(実際にはPydanticが十分賢い)ので、descriptiontaxtagsがデフォルト値と同じ値を持っていても、それらが明示的に設定された(デフォルトから取得されたのではなく)ことを認識します。

そのため、それらは JSON レスポンスに含まれます。

ヒント

デフォルト値は、None だけでなく、何でも構わないことに注意してください。

リスト([])、10.5float など。

response_model_include および response_model_exclude

パス操作デコレータのパラメータ response_model_include および response_model_exclude も使用できます。

これらは、含める属性(残りを省略する)または除外する属性(残りのすべてを含む)の名前を含む strset を受け取ります。

これは、Pydantic モデルが1つだけで、出力から一部のデータを削除したい場合に、簡単なショートカットとして使用できます。

ヒント

しかし、これらのパラメータを使用するよりも、複数のクラスを使用する上記の考え方を使用することが依然として推奨されます。

これは、response_model_includeresponse_model_exclude を使用して一部の属性を省略したとしても、アプリの OpenAPI (およびドキュメント) で生成される JSON Schema は完全なモデルのものであるためです。

これは、同様に機能する response_model_by_alias にも当てはまります。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items[item_id]
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items[item_id]

ヒント

構文 {"name", "description"} は、これら2つの値を持つ set を作成します。

これは set(["name", "description"]) と同等です。

set の代わりに list を使用する

set を使用するのを忘れて、代わりに list または tuple を使用した場合でも、FastAPI はそれを set に変換し、正しく機能します。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
    return items[item_id]
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
    "baz": {
        "name": "Baz",
        "description": "There goes my baz",
        "price": 50.2,
        "tax": 10.5,
    },
}


@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
    return items[item_id]

まとめ

レスポンスモデルを定義し、特にプライベートデータがフィルタリングされることを確実にするために、パス操作デコレータのパラメータresponse_modelを使用します。

明示的に設定された値のみを返すには、response_model_exclude_unsetを使用します。