在 FastAPI 中实现分页功能,可以结合数据库查询来返回数据的某一部分,而不是一次性加载所有数据。分页通常通过查询参数 skip
和 limit
来控制,skip
用于跳过前面的记录,limit
用于限制返回的记录数。下面是一个实现分页请求数据库中数据的示例,假设我们使用 SQLAlchemy 作为数据库 ORM 工具。
如果还没有安装相关的依赖,首先需要安装 FastAPI、Uvicorn 和 SQLAlchemy。
pip install fastapi uvicorn sqlalchemy databases
在这个示例中,我们将使用 SQLAlchemy 来定义数据库模型,并使用 databases
包来进行异步数据库操作。
from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.future import select
# 配置数据库连接
DATABASE_URL = "sqlite+aiosqlite:///./test.db" # 使用 SQLite 数据库
# 创建数据库连接引擎
engine = create_engine(DATABASE_URL, echo=True, future=True)
# 创建数据库会话
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, class_="AsyncSession")
# 创建模型基础类
Base = declarative_base()
# 定义数据模型
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
price = Column(Integer)
# 创建数据库表
Base.metadata.create_all(bind=engine)
接下来,我们将通过 FastAPI 路由提供分页功能,通过 skip
和 limit
参数来控制分页请求。
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from fastapi import FastAPI, Query, Depends
from typing import List
app = FastAPI()
# 获取数据库会话
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# 创建一个异步函数,支持分页查询
@app.get("/items/", response_model=List[Item])
async def read_items(skip: int = Query(0, alias="page", ge=0), limit: int = Query(10, le=100), db: AsyncSession = Depends(get_db)):
# 使用 SQLAlchemy 查询数据库
result = await db.execute(select(Item).offset(skip).limit(limit))
items = result.scalars().all() # 获取查询结果
return items
skip
: 用于跳过前面的记录。skip
默认为 0
,表示从第一个记录开始。limit
: 用于限制每次返回的数据条数。limit
默认为 10
,最大限制为 100
。select(Item).offset(skip).limit(limit)
:这部分查询语句通过 SQLAlchemy 的 offset
和 limit
来实现分页。offset(skip)
:跳过前 skip
条数据。limit(limit)
:限制返回 limit
条数据。.scalars().all()
来获取查询结果并返回为列表。使用 Uvicorn 启动 FastAPI 应用:
uvicorn app:app --reload
假设你的数据库中有多条 Item
数据,你可以通过以下方式进行分页测试。
GET /items/?page=0&limit=5
返回(假设返回 5 条数据):
[
{"id": 1, "name": "Item 1", "price": 100},
{"id": 2, "name": "Item 2", "price": 200},
{"id": 3, "name": "Item 3", "price": 300},
{"id": 4, "name": "Item 4", "price": 400},
{"id": 5, "name": "Item 5", "price": 500}
]
GET /items/?page=1&limit=5
返回(假设返回的是第 6 到第 10 条数据):
[
{"id": 6, "name": "Item 6", "price": 600},
{"id": 7, "name": "Item 7", "price": 700},
{"id": 8, "name": "Item 8", "price": 800},
{"id": 9, "name": "Item 9", "price": 900},
{"id": 10, "name": "Item 10", "price": 1000}
]
为了更好地实现分页功能,除了返回当前页面的数据,还可以返回总记录数(即数据库中符合条件的数据总条数)。这样,前端可以知道总页数,从而进行分页导航。
from fastapi import HTTPException
@app.get("/items/", response_model=List[Item])
async def read_items(
skip: int = Query(0, alias="page", ge=0),
limit: int = Query(10, le=100),
db: AsyncSession = Depends(get_db)
):
# 查询数据总数
result_count = await db.execute(select([Item]).with_only_columns([func.count()]))
total_items = result_count.scalar()
# 查询分页数据
result = await db.execute(select(Item).offset(skip).limit(limit))
items = result.scalars().all()
# 返回数据及分页信息
return {
"items": items,
"total_items": total_items,
"total_pages": (total_items + limit - 1) // limit,
"current_page": skip // limit + 1
}
{
"items": [
{"id": 1, "name": "Item 1", "price": 100},
{"id": 2, "name": "Item 2", "price": 200}
],
"total_items": 50,
"total_pages": 10,
"current_page": 1
}
通过 FastAPI 和 SQLAlchemy,我们可以非常方便地实现分页查询。核心的操作包括使用 offset
和 limit
来控制查询范围,以及通过查询参数 skip
和 limit
来控制分页。结合数据库的 count()
方法,还可以返回总记录数,进一步支持分页导航。