查询参数模型¶
如果你有一组具有相关性的查询参数,你可以创建一个 Pydantic 模型来声明它们。
这将允许你在多个地方去复用模型,并且一次性为所有参数声明验证和元数据。😎
注意
FastAPI 从 0.115.0 版本开始支持这个特性。🤓
使用 Pydantic 模型的查询参数¶
在一个 Pydantic 模型中声明你需要的查询参数,然后将参数声明为 Query:
from typing import Annotated, Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
return filter_query
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from typing import Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
return filter_query
FastAPI 将会从请求的查询参数中提取出每个字段的数据,并将你定义的 Pydantic 模型提供给你。
查看文档¶
你可以在 /docs 页面的 UI 中查看查询参数:
禁止额外的查询参数¶
在一些特殊的使用场景中(可能不是很常见),你可能希望限制你要接收的查询参数。
你可以使用 Pydantic 的模型配置来 forbid 任何 extra 字段:
from typing import Annotated, Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
model_config = {"extra": "forbid"}
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items/")
async def read_items(filter_query: Annotated[FilterParams, Query()]):
return filter_query
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from typing import Literal
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class FilterParams(BaseModel):
model_config = {"extra": "forbid"}
limit: int = Field(100, gt=0, le=100)
offset: int = Field(0, ge=0)
order_by: Literal["created_at", "updated_at"] = "created_at"
tags: list[str] = []
@app.get("/items/")
async def read_items(filter_query: FilterParams = Query()):
return filter_query
假设有一个客户端尝试在查询参数中发送一些额外的数据,它将会收到一个错误响应。
例如,如果客户端尝试发送一个值为 plumbus 的 tool 查询参数,如:
https://example.com/items/?limit=10&tool=plumbus
他们将收到一个错误响应,告诉他们查询参数 tool 是不允许的:
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["query", "tool"],
"msg": "Extra inputs are not permitted",
"input": "plumbus"
}
]
}
组合参数模型¶
你可以声明多个查询参数模型,并将它们与单独的查询参数或依赖项组合使用。
from typing import Annotated
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class Pagination(BaseModel):
limit: int = Field(10, ge=1)
offset: int = Field(0, ge=0)
class Filters(BaseModel):
tags: list[str] = []
@app.get("/items/")
def read_items(
pagination: Annotated[Pagination, Query()],
filters: Annotated[Filters, Query()],
search: str | None = None,
):
return {"pagination": pagination, "filters": filters, "search": search}
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class Pagination(BaseModel):
limit: int = Field(10, ge=1)
offset: int = Field(0, ge=0)
class Filters(BaseModel):
tags: list[str] = []
@app.get("/items/")
def read_items(
pagination: Pagination = Query(),
filters: Filters = Query(),
search: str | None = None,
):
return {"pagination": pagination, "filters": filters, "search": search}
请求参数仍处于同一层级。
https://example.com/items/?limit=5&offset=10&tags=red&tags=blue&search=shirt
查询字符串中没有 pagination 或 filters 键。每个模型都使用自己的默认值、字段验证器和模型验证器独立验证查询参数。在依赖项中声明模型或单独的参数时也是如此。
如果模型共享参数名或别名,每个模型都会独立验证该请求参数。对于相互独立的值,请使用不同的名称或别名。OpenAPI 只会列出共享参数一次,就像处理与依赖项共享的参数一样,因此共享参数的声明应描述相同的类型和约束。
每个模型的 extra 配置都适用于所有查询参数,包括在其他模型或依赖项中声明的参数。使用 extra="forbid" 时,该模型之外的任何参数都会被拒绝。使用 extra="allow" 时,这些参数会作为模型的额外数据被包含在内。
这同样适用于 Header 和 Cookie 模型。
可选查询参数模型¶
声明查询参数模型时,使用 | None 并将默认值设为 None,即可使这组参数变为可选。
from datetime import date
from typing import Annotated
from fastapi import FastAPI, Query
from pydantic import BaseModel
app = FastAPI()
class DateRange(BaseModel):
start: date
end: date
@app.get("/items/")
async def read_items(
limit: int = 10,
date_range: Annotated[DateRange | None, Query()] = None,
):
return {"limit": limit, "date_range": date_range}
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from datetime import date
from fastapi import FastAPI, Query
from pydantic import BaseModel
app = FastAPI()
class DateRange(BaseModel):
start: date
end: date
@app.get("/items/")
async def read_items(
limit: int = 10,
date_range: DateRange | None = Query(default=None),
):
return {"limit": limit, "date_range": date_range}
如果既未发送 start 也未发送 end,则 date_range 为 None。如果发送了其中任意一个,FastAPI 就会验证模型,并要求提供两个字段。
使用 | None 但不设置默认值时,该参数仍然是必需的。
额外参数遵循模型的 extra 配置。被忽略的额外参数不会使模型被视为已提供。
OpenAPI 会逐一列出这组参数的字段,并将它们标记为可选。
你也可以声明可选的 header 参数模型和 cookie 参数模型。
总结¶
你可以使用 Pydantic 模型在 FastAPI 中声明查询参数。😎
提示
剧透警告:你也可以使用 Pydantic 模型来声明 cookie 和 headers,但你将在本教程的后面部分阅读到这部分内容。🤫