2
2
# -*- coding: utf-8 -*-
3
3
from __future__ import annotations
4
4
5
- import math
6
-
5
+ from math import ceil
7
6
from typing import TYPE_CHECKING , Generic , Sequence , TypeVar
8
7
9
8
from fastapi import Depends , Query
10
9
from fastapi_pagination import pagination_ctx
11
10
from fastapi_pagination .bases import AbstractPage , AbstractParams , RawParams
12
11
from fastapi_pagination .ext .sqlalchemy import paginate
13
12
from fastapi_pagination .links .bases import create_links
14
- from pydantic import BaseModel
13
+ from pydantic import BaseModel , Field
15
14
16
15
if TYPE_CHECKING :
17
16
from sqlalchemy import Select
18
17
from sqlalchemy .ext .asyncio import AsyncSession
19
18
20
19
T = TypeVar ('T' )
21
- DataT = TypeVar ('DataT' )
22
20
SchemaT = TypeVar ('SchemaT' )
23
21
24
22
25
- class _Params (BaseModel , AbstractParams ):
23
+ class _CustomPageParams (BaseModel , AbstractParams ):
26
24
page : int = Query (1 , ge = 1 , description = 'Page number' )
27
25
size : int = Query (20 , gt = 0 , le = 100 , description = 'Page size' ) # 默认 20 条记录
28
26
@@ -33,53 +31,90 @@ def to_raw_params(self) -> RawParams:
33
31
)
34
32
35
33
36
- class _Page (AbstractPage [T ], Generic [T ]):
37
- items : Sequence [T ] # 数据
38
- total : int # 总数据数
39
- page : int # 第n页
40
- size : int # 每页数量
41
- total_pages : int # 总页数
42
- links : dict [str , str | None ] # 跳转链接
34
+ class _Links (BaseModel ):
35
+ first : str = Field (..., description = '首页链接' )
36
+ last : str = Field (..., description = '尾页链接' )
37
+ self : str = Field (..., description = '当前页链接' )
38
+ next : str | None = Field (None , description = '下一页链接' )
39
+ prev : str | None = Field (None , description = '上一页链接' )
40
+
41
+
42
+ class _PageDetails (BaseModel ):
43
+ items : list = Field ([], description = '当前页数据' )
44
+ total : int = Field (..., description = '总条数' )
45
+ page : int = Field (..., description = '当前页' )
46
+ size : int = Field (..., description = '每页数量' )
47
+ total_pages : int = Field (..., description = '总页数' )
48
+ links : _Links
49
+
43
50
44
- __params_type__ = _Params # 使用自定义的Params
51
+ class _CustomPage (_PageDetails , AbstractPage [T ], Generic [T ]):
52
+ __params_type__ = _CustomPageParams
45
53
46
54
@classmethod
47
55
def create (
48
56
cls ,
49
- items : Sequence [ T ] ,
57
+ items : list ,
50
58
total : int ,
51
- params : _Params ,
52
- ) -> _Page [T ]:
59
+ params : _CustomPageParams ,
60
+ ) -> _CustomPage [T ]:
53
61
page = params .page
54
62
size = params .size
55
- total_pages = math .ceil (total / params .size )
56
- links = create_links (** {
57
- 'first' : {'page' : 1 , 'size' : f'{ size } ' },
58
- 'last' : {'page' : f'{ math .ceil (total / params .size )} ' , 'size' : f'{ size } ' } if total > 0 else None ,
59
- 'next' : {'page' : f'{ page + 1 } ' , 'size' : f'{ size } ' } if (page + 1 ) <= total_pages else None ,
60
- 'prev' : {'page' : f'{ page - 1 } ' , 'size' : f'{ size } ' } if (page - 1 ) >= 1 else None ,
61
- }).model_dump ()
63
+ total_pages = ceil (total / params .size )
64
+ links = create_links (
65
+ first = {'page' : 1 , 'size' : size },
66
+ last = {'page' : f'{ ceil (total / params .size )} ' , 'size' : size } if total > 0 else {'page' : 1 , 'size' : size },
67
+ next = {'page' : f'{ page + 1 } ' , 'size' : size } if (page + 1 ) <= total_pages else None ,
68
+ prev = {'page' : f'{ page - 1 } ' , 'size' : size } if (page - 1 ) >= 1 else None ,
69
+ ).model_dump ()
70
+
71
+ return cls (
72
+ items = items ,
73
+ total = total ,
74
+ page = params .page ,
75
+ size = params .size ,
76
+ total_pages = total_pages ,
77
+ links = links , # type: ignore
78
+ )
79
+
62
80
63
- return cls (items = items , total = total , page = params .page , size = params .size , total_pages = total_pages , links = links )
81
+ class PageData (_PageDetails , Generic [SchemaT ]):
82
+ """
83
+ 包含 data schema 的统一返回模型,适用于分页接口
64
84
85
+ E.g. ::
86
+
87
+ @router.get('/test', response_model=ResponseSchemaModel[PageData[GetApiDetail]])
88
+ def test():
89
+ return ResponseSchemaModel[PageData[GetApiDetail]](data=GetApiDetail(...))
90
+
91
+
92
+ @router.get('/test')
93
+ def test() -> ResponseSchemaModel[PageData[GetApiDetail]]:
94
+ return ResponseSchemaModel[PageData[GetApiDetail]](data=GetApiDetail(...))
95
+
96
+
97
+ @router.get('/test')
98
+ def test() -> ResponseSchemaModel[PageData[GetApiDetail]]:
99
+ res = CustomResponseCode.HTTP_200
100
+ return ResponseSchemaModel[PageData[GetApiDetail]](code=res.code, msg=res.msg, data=GetApiDetail(...))
101
+ """
65
102
66
- class _PageData (BaseModel , Generic [DataT ]):
67
- page_data : DataT | None = None
103
+ items : Sequence [SchemaT ]
68
104
69
105
70
- async def paging_data (db : AsyncSession , select : Select , page_data_schema : SchemaT ) -> dict :
106
+ async def paging_data (db : AsyncSession , select : Select ) -> dict :
71
107
"""
72
108
基于 SQLAlchemy 创建分页数据
73
109
74
110
:param db:
75
111
:param select:
76
- :param page_data_schema:
77
112
:return:
78
113
"""
79
- _paginate = await paginate (db , select )
80
- page_data = _PageData [ _Page [ page_data_schema ]]( page_data = _paginate ) .model_dump ()[ 'page_data' ]
114
+ paginated_data : _CustomPage = await paginate (db , select )
115
+ page_data = paginated_data .model_dump ()
81
116
return page_data
82
117
83
118
84
119
# 分页依赖注入
85
- DependsPagination = Depends (pagination_ctx (_Page ))
120
+ DependsPagination = Depends (pagination_ctx (_CustomPage ))
0 commit comments