Files
qhp13654398483 cf693cdce6 Fix serialization when pagination is empty (#491)
* bug fix

* update codes
2025-01-18 20:47:31 +08:00

123 lines
3.8 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations
from math import ceil
from typing import TYPE_CHECKING, Generic, Sequence, TypeVar
from fastapi import Depends, Query
from fastapi_pagination import pagination_ctx
from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams
from fastapi_pagination.ext.sqlalchemy import paginate
from fastapi_pagination.links.bases import create_links
from pydantic import BaseModel, Field
if TYPE_CHECKING:
from sqlalchemy import Select
from sqlalchemy.ext.asyncio import AsyncSession
T = TypeVar('T')
SchemaT = TypeVar('SchemaT')
class _CustomPageParams(BaseModel, AbstractParams):
page: int = Query(1, ge=1, description='Page number')
size: int = Query(20, gt=0, le=100, description='Page size') # 默认 20 条记录
def to_raw_params(self) -> RawParams:
return RawParams(
limit=self.size,
offset=self.size * (self.page - 1),
)
class _Links(BaseModel):
first: str = Field(..., description='首页链接')
last: str = Field(..., description='尾页链接')
self: str = Field(..., description='当前页链接')
next: str | None = Field(None, description='下一页链接')
prev: str | None = Field(None, description='上一页链接')
class _PageDetails(BaseModel):
items: list = Field([], description='当前页数据')
total: int = Field(..., description='总条数')
page: int = Field(..., description='当前页')
size: int = Field(..., description='每页数量')
total_pages: int = Field(..., description='总页数')
links: _Links
class _CustomPage(_PageDetails, AbstractPage[T], Generic[T]):
__params_type__ = _CustomPageParams
@classmethod
def create(
cls,
items: list,
total: int,
params: _CustomPageParams,
) -> _CustomPage[T]:
page = params.page
size = params.size
total_pages = ceil(total / params.size)
links = create_links(
first={'page': 1, 'size': size},
last={'page': f'{ceil(total / params.size)}', 'size': size} if total > 0 else {'page': 1, 'size': size},
next={'page': f'{page + 1}', 'size': size} if (page + 1) <= total_pages else None,
prev={'page': f'{page - 1}', 'size': size} if (page - 1) >= 1 else None,
).model_dump()
return cls(
items=items,
total=total,
page=params.page,
size=params.size,
total_pages=total_pages,
links=links, # type: ignore
)
class PageData(_PageDetails, Generic[SchemaT]):
"""
包含 data schema 的统一返回模型,适用于分页接口
E.g. ::
@router.get('/test', response_model=ResponseSchemaModel[PageData[GetApiListDetails]])
def test():
return ResponseSchemaModel[PageData[GetApiListDetails]](data=GetApiListDetails(...))
@router.get('/test')
def test() -> ResponseSchemaModel[PageData[GetApiListDetails]]:
return ResponseSchemaModel[PageData[GetApiListDetails]](data=GetApiListDetails(...))
@router.get('/test')
def test() -> ResponseSchemaModel[PageData[GetApiListDetails]]:
res = CustomResponseCode.HTTP_200
return ResponseSchemaModel[PageData[GetApiListDetails]](
code=res.code, msg=res.msg, data=GetApiListDetails(...)
)
"""
items: Sequence[SchemaT]
async def paging_data(db: AsyncSession, select: Select) -> dict:
"""
基于 SQLAlchemy 创建分页数据
:param db:
:param select:
:return:
"""
paginated_data: _CustomPage = await paginate(db, select)
page_data = paginated_data.model_dump()
return page_data
# 分页依赖注入
DependsPagination = Depends(pagination_ctx(_CustomPage))