第5章 - 查询参数
嗨,朋友!我是长安。
这一章我们来讲查询参数。说实话,查询参数是我工作中用得最多的功能之一,像分页、搜索、筛选都靠它!
🎯 本章目标
- 理解什么是查询参数
- 学会定义必选和可选查询参数
- 掌握查询参数的默认值
- 学会使用 Query 进行验证
1️⃣ 什么是查询参数?
查询参数是 URL 中 ? 后面的部分,用 & 分隔多个参数。
比如:
/users?page=1- page 是查询参数/users?page=1&size=10- page 和 size 都是查询参数/search?keyword=python&sort=date- keyword 和 sort 是查询参数
2️⃣ 定义查询参数
在函数参数中,不属于路径参数的参数就是查询参数:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items")
def get_items(page: int, size: int):
return {
"page": page,
"size": size
}
访问 http://127.0.0.1:8000/items?page=1&size=10,返回:
{"page": 1, "size": 10}
长安提醒
上面的例子中,page 和 size 都是必选参数,不传会报错! 这是很多新手容易忘记的点,我当年也经常犯这个错误。
3️⃣ 可选参数和默认值
设置默认值
@app.get("/items")
def get_items(page: int = 1, size: int = 10):
return {"page": page, "size": size}
现在:
- 访问
/items→{"page": 1, "size": 10} - 访问
/items?page=2→{"page": 2, "size": 10} - 访问
/items?page=2&size=20→{"page": 2, "size": 20}
可选参数(可以为 None)
from typing import Optional
@app.get("/search")
def search(keyword: Optional[str] = None):
if keyword:
return {"message": f"搜索:{keyword}"}
return {"message": "请输入搜索关键词"}
或者使用 Python 3.10+ 的语法:
@app.get("/search")
def search(keyword: str | None = None):
if keyword:
return {"message": f"搜索:{keyword}"}
return {"message": "请输入搜索关键词"}
4️⃣ 必选 vs 可选
from typing import Optional
@app.get("/users")
def get_users(
page: int, # 必选,无默认值
size: int = 10, # 可选,有默认值
keyword: Optional[str] = None # 可选,可以为 None
):
return {
"page": page,
"size": size,
"keyword": keyword
}
| 参数 | 是否必选 | 说明 |
|---|---|---|
page: int | 必选 | 没有默认值 |
size: int = 10 | 可选 | 有默认值 10 |
keyword: Optional[str] = None | 可选 | 默认为 None |
5️⃣ 路径参数 + 查询参数
可以同时使用路径参数和查询参数:
@app.get("/users/{user_id}/posts")
def get_user_posts(
user_id: int, # 路径参数
page: int = 1, # 查询参数
size: int = 10 # 查询参数
):
return {
"user_id": user_id,
"page": page,
"size": size
}
访问 /users/1/posts?page=2&size=5:
{"user_id": 1, "page": 2, "size": 5}
FastAPI 会自动区分:
- 在路径中定义的
{user_id}→ 路径参数 - 其他参数 → 查询参数
6️⃣ 使用 Query 进行验证
使用 Query 可以对查询参数进行更详细的验证:
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items")
def get_items(
page: int = Query(default=1, ge=1, description="页码"),
size: int = Query(default=10, ge=1, le=100, description="每页数量")
):
return {"page": page, "size": size}
Query 的常用参数
| 参数 | 说明 | 示例 |
|---|---|---|
default | 默认值 | default=1 |
ge | 大于等于 | ge=1 |
gt | 大于 | gt=0 |
le | 小于等于 | le=100 |
lt | 小于 | lt=100 |
min_length | 最小长度 | min_length=1 |
max_length | 最大长度 | max_length=50 |
pattern | 正则表达式 | pattern="^[a-z]+$" |
title | 标题 | title="页码" |
description | 描述 | description="当前页码" |
example | 示例值 | example=1 |
字符串验证示例
@app.get("/search")
def search(
keyword: str = Query(
min_length=1,
max_length=50,
description="搜索关键词"
)
):
return {"keyword": keyword}
必选查询参数
使用 ... 表示必选:
@app.get("/search")
def search(
keyword: str = Query(..., min_length=1, description="搜索关键词")
):
return {"keyword": keyword}
... 是 Python 的 Ellipsis 对象,在这里表示"必须提供值"。
7️⃣ 接收列表参数
有时候需要接收多个相同名称的参数:
from typing import List
@app.get("/items")
def get_items(ids: List[int] = Query(default=[])):
return {"ids": ids}
访问 /items?ids=1&ids=2&ids=3:
{"ids": [1, 2, 3]}
带默认值的列表
@app.get("/items")
def get_items(
ids: List[int] = Query(default=[1, 2, 3])
):
return {"ids": ids}
8️⃣ 参数别名
有时候前端传的参数名和 Python 变量名不一样(比如用连字符):
@app.get("/items")
def get_items(
item_type: str = Query(alias="item-type")
):
return {"item_type": item_type}
访问 /items?item-type=book:
{"item_type": "book"}
9️⃣ 废弃参数
标记某个参数即将废弃:
@app.get("/items")
def get_items(
old_param: str = Query(default=None, deprecated=True),
new_param: str = Query(default=None)
):
return {"old": old_param, "new": new_param}
在文档中会显示该参数已废弃。
🔟 完整示例
from typing import List, Optional
from fastapi import FastAPI, Query
app = FastAPI(title="查询参数示例")
# 模拟数据
fake_items = [
{"id": 1, "name": "苹果", "price": 5.0, "category": "水果"},
{"id": 2, "name": "香蕉", "price": 3.0, "category": "水果"},
{"id": 3, "name": "牛奶", "price": 8.0, "category": "饮品"},
{"id": 4, "name": "面包", "price": 6.0, "category": "食品"},
{"id": 5, "name": "可乐", "price": 4.0, "category": "饮品"},
]
@app.get("/items", tags=["商品"])
def get_items(
page: int = Query(default=1, ge=1, description="页码"),
size: int = Query(default=10, ge=1, le=100, description="每页数量"),
keyword: Optional[str] = Query(default=None, min_length=1, description="搜索关键词"),
category: Optional[str] = Query(default=None, description="商品分类"),
min_price: Optional[float] = Query(default=None, ge=0, description="最低价格"),
max_price: Optional[float] = Query(default=None, ge=0, description="最高价格"),
):
"""
获取商品列表
支持分页、搜索、分类筛选、价格筛选
"""
result = fake_items.copy()
# 关键词搜索
if keyword:
result = [item for item in result if keyword in item["name"]]
# 分类筛选
if category:
result = [item for item in result if item["category"] == category]
# 价格筛选
if min_price is not None:
result = [item for item in result if item["price"] >= min_price]
if max_price is not None:
result = [item for item in result if item["price"] <= max_price]
# 分页
start = (page - 1) * size
end = start + size
paginated = result[start:end]
return {
"total": len(result),
"page": page,
"size": size,
"data": paginated
}
@app.get("/items/batch", tags=["商品"])
def get_items_batch(
ids: List[int] = Query(description="商品ID列表")
):
"""批量获取商品"""
result = [item for item in fake_items if item["id"] in ids]
return {"items": result}
@app.get("/search", tags=["搜索"])
def search(
q: str = Query(..., min_length=1, max_length=100, description="搜索内容"),
type: str = Query(default="all", description="搜索类型"),
sort: str = Query(default="relevance", description="排序方式")
):
"""搜索接口"""
return {
"query": q,
"type": type,
"sort": sort,
"results": []
}
# 路径参数 + 查询参数
@app.get("/categories/{category}/items", tags=["分类"])
def get_category_items(
category: str,
page: int = Query(default=1, ge=1),
size: int = Query(default=10, ge=1, le=50)
):
"""获取分类下的商品"""
result = [item for item in fake_items if item["category"] == category]
return {
"category": category,
"total": len(result),
"page": page,
"size": size,
"data": result
}
📝 小结
本章我们学习了:
- ✅ 查询参数的定义方式
- ✅ 必选参数和可选参数
- ✅ 默认值的设置
- ✅ 使用 Query 进行验证
- ✅ 接收列表参数
- ✅ 参数别名
🏃 下一步
现在你会处理 GET 请求的参数了,但是 POST 请求通常需要在请求体中传递数据。下一章学习请求体!
💪 练习题
创建一个用户列表接口
/users,支持以下查询参数:- page: 页码,默认1,最小1
- size: 每页数量,默认10,范围1-100
- name: 用户名搜索,可选
- status: 用户状态,可选
创建一个搜索接口
/search,要求:- keyword: 必选,长度1-50
- type: 可选,默认"all"
- page: 可选,默认1
创建一个批量查询接口
/users/batch,接收多个用户ID
参考答案
from typing import List, Optional
from fastapi import FastAPI, Query
app = FastAPI()
# 练习1
@app.get("/users")
def get_users(
page: int = Query(default=1, ge=1),
size: int = Query(default=10, ge=1, le=100),
name: Optional[str] = Query(default=None),
status: Optional[str] = Query(default=None)
):
return {
"page": page,
"size": size,
"name": name,
"status": status
}
# 练习2
@app.get("/search")
def search(
keyword: str = Query(..., min_length=1, max_length=50),
type: str = Query(default="all"),
page: int = Query(default=1)
):
return {
"keyword": keyword,
"type": type,
"page": page
}
# 练习3
@app.get("/users/batch")
def get_users_batch(
ids: List[int] = Query(description="用户ID列表")
):
return {"ids": ids}
