Skip to content

给列表端点加分页

目标:让一个列表端点接受 ?offset=?limit=?desc=?order=?created_after_datetime= 等查询参数,并返回 {count, items} 形式的响应。

前置条件

  • 你已经有一个 FastAPI 端点
  • 你的模型继承了 TableBaseMixinUUIDTableBaseMixin
  • 你有一个 XxxResponse DTO

1. 把请求参数声明为 FastAPI 依赖

python
from typing import Annotated
from fastapi import Depends
from sqlmodel_ext import TableViewRequest

TableViewDep = Annotated[TableViewRequest, Depends()]

TableViewRequest 同时包含分页(offset / limit / desc / order)和时间过滤(created_after_datetime / created_before_datetime / updated_after_datetime / updated_before_datetime)。FastAPI 的 Depends() 会自动把查询字符串解析成这个对象。

2. 在端点中调用 get_with_count()

python
from sqlmodel_ext import ListResponse

@router.get("", response_model=ListResponse[ArticleResponse])
async def list_articles(
    session: SessionDep,
    table_view: TableViewDep,
) -> ListResponse[Article]:
    return await Article.get_with_count(
        session,
        Article.is_published == True,
        table_view=table_view,
    )

get_with_count() 内部并行执行 COUNT(*)SELECT ... LIMIT N OFFSET M,组装成 ListResponse[T] 返回。

3. 客户端怎么调用

http
GET /articles?offset=0&limit=20&desc=true&order=created_at&created_after_datetime=2026-01-01T00:00:00

返回:

json
{
  "count": 142,
  "items": [
    { "id": "...", "title": "...", "created_at": "...", "..." : "..." }
  ]
}

默认值

参数默认值上限
offset0
limit50100
descTrue
order"created_at"仅支持 "created_at""updated_at"

如果你需要按其他字段排序,跳过 table_view,自己传 order_by=get() / get_with_count()

常见陷阱

  • response_model 必须用 ListResponse[ArticleResponse],不能写成 list[ArticleResponse]——后者会让 FastAPI 把 count 字段也当成 list item 来序列化。
  • 时间区间是左闭右开 [after, before)created_after_datetime=2026-01-01 + created_before_datetime=2026-02-01 表示"整个 1 月"。
  • order 只能是 created_atupdated_atLiteral 限制)。如果你的客户端传了别的字符串,FastAPI 会返回 422 Unprocessable Entity

相关参考