与在路径操作函数中使用 QueryPathBody 声明校验与元数据的方式一样,可以使用 Pydantic 的 Field 在 Pydantic 模型内部声明校验和元数据。

导入 Field

首先,从 Pydantic 中导入 Field

Python 3.10+Python 3.9+Python 3.8+Python 3.10+ non-AnnotatedPython 3.8+ non-Annotated

  1. from typing import Annotated
  2. from fastapi import Body, FastAPI
  3. from pydantic import BaseModel, Field
  4. app = FastAPI()
  5. class Item(BaseModel):
  6. name: str
  7. description: str | None = Field(
  8. default=None, title="The description of the item", max_length=300
  9. )
  10. price: float = Field(gt=0, description="The price must be greater than zero")
  11. tax: float | None = None
  12. @app.put("/items/{item_id}")
  13. async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
  14. results = {"item_id": item_id, "item": item}
  15. return results`
  1. from typing import Annotated, Union
  2. from fastapi import Body, FastAPI
  3. from pydantic import BaseModel, Field
  4. app = FastAPI()
  5. class Item(BaseModel):
  6. name: str
  7. description: Union[str, None] = Field(
  8. default=None, title="The description of the item", max_length=300
  9. )
  10. price: float = Field(gt=0, description="The price must be greater than zero")
  11. tax: Union[float, None] = None
  12. @app.put("/items/{item_id}")
  13. async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
  14. results = {"item_id": item_id, "item": item}
  15. return results`
  1. from typing import Union
  2. from fastapi import Body, FastAPI
  3. from pydantic import BaseModel, Field from typing_extensions import Annotated
  4. app = FastAPI()
  5. class Item(BaseModel):
  6. name: str
  7. description: Union[str, None] = Field(
  8. default=None, title="The description of the item", max_length=300
  9. )
  10. price: float = Field(gt=0, description="The price must be greater than zero")
  11. tax: Union[float, None] = None
  12. @app.put("/items/{item_id}")
  13. async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
  14. results = {"item_id": item_id, "item": item}
  15. return results`

Tip

尽可能选择使用 Annotated 的版本。

  1. from fastapi import Body, FastAPI
  2. from pydantic import BaseModel, Field
  3. app = FastAPI()
  4. class Item(BaseModel):
  5. name: str
  6. description: str | None = Field(
  7. default=None, title="The description of the item", max_length=300
  8. )
  9. price: float = Field(gt=0, description="The price must be greater than zero")
  10. tax: float | None = None
  11. @app.put("/items/{item_id}")
  12. async def update_item(item_id: int, item: Item = Body(embed=True)):
  13. results = {"item_id": item_id, "item": item}
  14. return results`

Tip

尽可能选择使用 Annotated 的版本。

  1. from typing import Union
  2. from fastapi import Body, FastAPI
  3. from pydantic import BaseModel, Field
  4. app = FastAPI()
  5. class Item(BaseModel):
  6. name: str
  7. description: Union[str, None] = Field(
  8. default=None, title="The description of the item", max_length=300
  9. )
  10. price: float = Field(gt=0, description="The price must be greater than zero")
  11. tax: Union[float, None] = None
  12. @app.put("/items/{item_id}")
  13. async def update_item(item_id: int, item: Item = Body(embed=True)):
  14. results = {"item_id": item_id, "item": item}
  15. return results`

警告

注意,与从 fastapi 导入 QueryPathBody 不同,要直接从 pydantic 导入 Field

声明模型属性

然后,使用 Field 定义模型的属性:

Python 3.10+Python 3.9+Python 3.8+Python 3.10+ non-AnnotatedPython 3.8+ non-Annotated

  1. from typing import Annotated
  2. from fastapi import Body, FastAPI
  3. from pydantic import BaseModel, Field
  4. app = FastAPI()
  5. class Item(BaseModel):
  6. name: str
  7. description: str | None = Field( default=None, title="The description of the item", max_length=300 ) price: float = Field(gt=0, description="The price must be greater than zero") tax: float | None = None
  8. @app.put("/items/{item_id}")
  9. async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
  10. results = {"item_id": item_id, "item": item}
  11. return results`
  1. from typing import Annotated, Union
  2. from fastapi import Body, FastAPI
  3. from pydantic import BaseModel, Field
  4. app = FastAPI()
  5. class Item(BaseModel):
  6. name: str
  7. description: Union[str, None] = Field( default=None, title="The description of the item", max_length=300 ) price: float = Field(gt=0, description="The price must be greater than zero") tax: Union[float, None] = None
  8. @app.put("/items/{item_id}")
  9. async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
  10. results = {"item_id": item_id, "item": item}
  11. return results`
  1. from typing import Union
  2. from fastapi import Body, FastAPI
  3. from pydantic import BaseModel, Field
  4. from typing_extensions import Annotated
  5. app = FastAPI()
  6. class Item(BaseModel):
  7. name: str
  8. description: Union[str, None] = Field( default=None, title="The description of the item", max_length=300 ) price: float = Field(gt=0, description="The price must be greater than zero") tax: Union[float, None] = None
  9. @app.put("/items/{item_id}")
  10. async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
  11. results = {"item_id": item_id, "item": item}
  12. return results`

Tip

Prefer to use the Annotated version if possible.

  1. from fastapi import Body, FastAPI
  2. from pydantic import BaseModel, Field
  3. app = FastAPI()
  4. class Item(BaseModel):
  5. name: str
  6. description: str | None = Field( default=None, title="The description of the item", max_length=300 ) price: float = Field(gt=0, description="The price must be greater than zero") tax: float | None = None
  7. @app.put("/items/{item_id}")
  8. async def update_item(item_id: int, item: Item = Body(embed=True)):
  9. results = {"item_id": item_id, "item": item}
  10. return results`

Tip

Prefer to use the Annotated version if possible.

  1. from typing import Union
  2. from fastapi import Body, FastAPI
  3. from pydantic import BaseModel, Field
  4. app = FastAPI()
  5. class Item(BaseModel):
  6. name: str
  7. description: Union[str, None] = Field( default=None, title="The description of the item", max_length=300 ) price: float = Field(gt=0, description="The price must be greater than zero") tax: Union[float, None] = None
  8. @app.put("/items/{item_id}")
  9. async def update_item(item_id: int, item: Item = Body(embed=True)):
  10. results = {"item_id": item_id, "item": item}
  11. return results`

Field 的工作方式和 QueryPathBody 相同,参数也相同。

技术细节

实际上,QueryPath 都是 Params 的子类,而 Params 类又是 Pydantic 中 FieldInfo 的子类。

Pydantic 的 Field 返回也是 FieldInfo 的类实例。

Body 直接返回的也是 FieldInfo 的子类的对象。后文还会介绍一些 Body 的子类。

注意,从 fastapi 导入的 QueryPath 等对象实际上都是返回特殊类的函数。

提示

注意,模型属性的类型、默认值及 Field 的代码结构与路径操作函数的参数相同,只不过是用 Field 替换了PathQueryBody

添加更多信息

FieldQueryBody 等对象里可以声明更多信息,并且 JSON Schema 中也会集成这些信息。

声明示例一章中将详细介绍添加更多信息的知识。

小结

Pydantic 的 Field 可以为模型属性声明更多校验和元数据。

传递 JSON Schema 元数据还可以使用更多关键字参数。