Files
djangoadmin-wiki/DjangoAdmin_FastAPI_EleVue/zh/volume7/pagination.md
T
2026-09-12 23:12:15 +08:00

4.4 KiB
Raw Blame History

分页规范

概述

统一使用 pageNo/pageSize 作为分页参数,BaseService.get_page() 封装分页逻辑,返回标准分页结构。

:::tip 分页约定

  • 前端传递 pageNo(页码,从 1 开始)和 pageSize(每页条数)
  • 后端返回 {records, total, size, current, pages} 结构
  • 默认每页 10 条(PAGE_SIZE = 10) :::

请求参数

参数 类型 必填 默认值 说明
pageNo int 否 1 页码,从 1 开始
pageSize int 否 10 每页条数

请求示例

# GET 请求
GET /api/v1/position/page?pageNo=1&pageSize=10

# 带筛选条件
GET /api/v1/position/page?pageNo=1&pageSize=10&name=开发&status=1

响应格式

{
    "code": 0,
    "data": {
        "records": [
            {
                "id": 1,
                "name": "开发工程师",
                "status": 1,
                "statusText": "在用",
                "sort": 1,
                "createTime": "2026-01-01 10:00:00"
            }
        ],
        "total": 50,
        "size": 10,
        "current": 1,
        "pages": 5
    },
    "msg": "操作成功",
    "ok": true
}
字段 类型 说明
records array 当前页数据列表
total int 总记录数
size int 每页条数
current int 当前页码
pages int 总页数

BaseService 实现

get_page 方法

def get_page(self, request) -> R:
    """分页查询"""
    page, limit = parse_pagination(request)
    query = self._build_page_query(request)
    items, total = self.repo.paginate(page, limit, query)

    # 超页自动回退第 1 页
    if page > 1 and not items:
        page = 1
        items, total = self.repo.paginate(page, limit, query)

    result = [self._serialize(item) for item in items]
    return R.page(result, total, page, limit)

:::tip 超页回退 当请求的页码超过总页数时(如删除数据后前端未刷新),自动回退到第 1 页,避免返回空数据。 :::

分页查询构建

def _build_page_query(self, request):
    """组装分页查询:声明字段过滤 + 排序"""
    query = self.repo.filter()
    query = self._apply_page_filters(query, request)

    for field_name, direction in self.page_order_by:
        column = getattr(self.model, field_name)
        query = query.order_by(column.asc() if direction == 'asc' else column.desc())

    return query

字段过滤声明

class PositionService(BaseService[Position]):
    # 模糊查询字段:name LIKE %值%
    page_like_fields = ('name',)
    # 等值查询字段:status = 值
    page_eq_fields = ('status',)
    # 排序规则
    page_order_by = (('sort', 'asc'),)

:::tip 过滤字段说明
- `page_like_fields`:从 query_params 取同名参数,自动应用 `LIKE %值%`,空值跳过
- `page_eq_fields`:从 query_params 取同名参数,自动应用 `=值`,空值跳过
- 两种字段均自动跳过 None/空字符串,无需手动判断
:::

## Repository 分页实现

```python
def paginate(self, page_no: int = 1, page_size: int = PAGE_SIZE, query=None):
    """分页查询,返回 (items, total)"""
    q = query if query is not None else self.db.query(self.model)
    q = q.filter(self._soft_col() == 0)  # 自动软删过滤
    total = q.count()
    items = q.limit(page_size).offset((page_no - 1) * page_size).all()
    return items, total

Endpoint 示例

@router.get('/page', summary='查询分页数据')
@permission_required("sys:position:page")
def page(request: Request):
    """获取岗位分页列表"""
    return position_service.get_page(request)

:::tip 权限标识 分页接口权限标识格式:sys:{模块名}:page :::

自定义分页过滤

子类可覆盖 _apply_page_filters 添加固定过滤条件:

def _apply_page_filters(self, query, request):
    """添加固定过滤:只显示启用状态的数据"""
    query = super()._apply_page_filters(query, request)
    query = query.filter(self.model.status == 1)
    return query

总结

分页规范统一使用 pageNo/pageSize 参数,通过 BaseService.get_page() 自动处理分页逻辑。子类只需声明 page_like_fields/page_eq_fields/page_order_by 即可获得完整的分页能力。响应使用 R.page() 封装标准分页结构,前端可直接使用 records/total/pages 渲染列表和分页组件。