路径参数和数字校验¶
正如你可以使用 Query 为查询参数声明更多的校验和元数据一样,你也可以使用 Path 为路径参数声明相同类型的校验和元数据。
导入 Path¶
首先,从 fastapi 中导入 Path,并导入 Annotated。
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[str | None, Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: str | None = Query(default=None, alias="item-query"),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
信息
FastAPI 在 0.95.0 版本中添加了对 Annotated 的支持(并开始推荐它)。
如果您的版本较旧,尝试使用 Annotated 时会出错。
在使用 Annotated 之前,请确保将 FastAPI 版本升级到至少 0.95.1。
声明元数据¶
你可以声明与 Query 相同的参数。
例如,要为路径参数 item_id 声明一个 title 元数据值,你可以这样写:
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[str | None, Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: str | None = Query(default=None, alias="item-query"),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
注意
路径参数总是必需的,因为它是路径的一部分。即使你用 None 声明它或设置了默认值,这也不会产生任何影响,它仍然必须是必需的。
按需排列参数¶
提示
如果你使用 Annotated,这一点可能就不那么重要或必要了。
假设你想要将查询参数 q 声明为必需的 str 类型。
而且你不需要为该参数声明其他任何内容,所以你实际上不需要使用 Query。
但你仍然需要为路径参数 item_id 使用 Path。并且由于某种原因,你不想使用 Annotated。
如果你将带有“默认值”的参数放在没有“默认值”的参数之前,Python 会报错。
但你可以重新排列它们,让没有默认值的参数(查询参数 q)在前。
这对 FastAPI 来说没有关系。它会根据参数的名称、类型和默认值声明(Query、Path 等)来检测参数,它不关心顺序。
因此,你可以这样声明函数:
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
但请记住,如果你使用 Annotated,就不会遇到这个问题,因为你没有使用函数参数默认值作为 Query() 或 Path() 的参数,所以顺序无关紧要。
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
按需排列参数,技巧¶
提示
如果你使用 Annotated,这一点可能就不那么重要或必要了。
这是一个小技巧,可能会派上用场,但你不会经常用到它。
如果你想要:
- 声明查询参数
q,且不使用Query或任何默认值 - 使用
Path声明路径参数item_id - 按不同顺序排列它们
- 不使用
Annotated
...Python 为此有一个小小的特殊语法。
将 * 作为函数的第一个参数传递。
Python 不会对这个 * 做任何处理,但它会知道随后的所有参数都应作为关键字参数(键值对)调用,也称为 kwargs。即使它们没有默认值也是如此。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
使用 Annotated 效果更佳¶
请记住,如果你使用 Annotated,由于你没有使用函数参数的默认值,因此不会遇到这个问题,你可能也不需要使用 *。
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
数字校验:大于等于¶
使用 Query 和 Path(以及稍后你会看到的其他内容),你可以声明数字约束。
这里,使用 ge=1,item_id 必须是一个“greater than or equal”(大于或等于)1 的整数。
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=1)], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*, item_id: int = Path(title="The ID of the item to get", ge=1), q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
数字校验:大于且小于等于¶
同样适用于:
gt:greaterthan(大于)le:less than orequal(小于或等于)
from typing import Annotated
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", gt=0, le=1000)],
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(title="The ID of the item to get", gt=0, le=1000),
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
数字校验:浮点数,大于且小于¶
数字校验也适用于 float 值。
这就是能够声明 gt 而不仅仅是 ge 的重要之处。有了它,你可以要求例如值必须大于 0,即使它小于 1。
因此,0.5 将是一个有效值。但 0.0 或 0 则不是。
对于 lt 也是同样的道理。
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: str,
size: Annotated[float, Query(gt=0, lt=10.5)],
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if size:
results.update({"size": size})
return results
🤓 其他版本和变体
提示
如果可能,请优先使用 Annotated 版本。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*,
item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
q: str,
size: float = Query(gt=0, lt=10.5),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if size:
results.update({"size": size})
return results
总结¶
使用 Query、Path(以及你尚未看到的其他类),你可以按照与 查询参数和字符串校验 相同的方式声明元数据和字符串校验。
你也可以声明数字校验:
gt:greaterthan(大于)ge:greater than orequal(大于或等于)lt:lessthan(小于)le:less than orequal(小于或等于)
信息
Query、Path 以及你稍后会看到的其他类都是通用 Param 类的子类。
它们共享相同的参数用于实现你所见过的额外校验和元数据。
技术细节
当你从 fastapi 导入 Query、Path 等时,它们实际上是函数。
当调用它们时,它们会返回同名类的实例。
所以,你导入了 Query,它是一个函数。当你调用它时,它返回一个也叫 Query 的类的实例。
这些函数之所以存在(而不是直接使用类),是为了让你的编辑器不会对它们的类型报错。
这样你就可以使用正常的编辑器和编码工具,而无需添加自定义配置来忽略这些错误。