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

161 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 分页规范
## 概述
统一使用 `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 | 每页条数 |
### 请求示例
```bash
# GET 请求
GET /api/v1/position/page?pageNo=1&pageSize=10
# 带筛选条件
GET /api/v1/position/page?pageNo=1&pageSize=10&name=开发&status=1
```
## 响应格式
```json
{
"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 方法
```python
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 页,避免返回空数据。
:::
### 分页查询构建
```python
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
```
### 字段过滤声明
```python
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 示例
```python
@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` 添加固定过滤条件:
```python
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` 渲染列表和分页组件。