mirror of
https://github.com/fastapi-practices/fastapi_best_architecture.git
synced 2025-08-15 03:52:54 +08:00

* define the basic architecture * Update script and deployment file locations * Update the route registration * Fix CI download dependencies * Updated ruff to 0.3.3 * Update app subdirectory naming * Update the model import * fix pre-commit pdm lock * Update the service directory naming * Add CRUD method documents * Fix the issue of circular import * Update the README document * Update the SQL statement for create tables * Update docker scripts and documentation * Fix docker scripts * Update the backend README.md * Add the security folder and move the redis client * Update the configuration item * Fix environment configuration reads * Update the default configuration * Updated README description * Updated the user registration API * Fix test cases * Update the celery configuration * Update and fix celery configuration * Updated the celery structure * Update celery tasks and api * Add celery flower * Update the import style * Update contributors
86 lines
2.6 KiB
Python
86 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
|
|
from typing import TYPE_CHECKING, Dict, 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
|
|
|
|
if TYPE_CHECKING:
|
|
from sqlalchemy import Select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
T = TypeVar('T')
|
|
DataT = TypeVar('DataT')
|
|
SchemaT = TypeVar('SchemaT')
|
|
|
|
|
|
class _Params(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 _Page(AbstractPage[T], Generic[T]):
|
|
items: Sequence[T] # 数据
|
|
total: int # 总数据数
|
|
page: int # 第n页
|
|
size: int # 每页数量
|
|
total_pages: int # 总页数
|
|
links: Dict[str, str | None] # 跳转链接
|
|
|
|
__params_type__ = _Params # 使用自定义的Params
|
|
|
|
@classmethod
|
|
def create(
|
|
cls,
|
|
items: Sequence[T],
|
|
total: int,
|
|
params: _Params,
|
|
) -> _Page[T]:
|
|
page = params.page
|
|
size = params.size
|
|
total_pages = math.ceil(total / params.size)
|
|
links = create_links(**{
|
|
'first': {'page': 1, 'size': f'{size}'},
|
|
'last': {'page': f'{math.ceil(total / params.size)}', 'size': f'{size}'} if total > 0 else None,
|
|
'next': {'page': f'{page + 1}', 'size': f'{size}'} if (page + 1) <= total_pages else None,
|
|
'prev': {'page': f'{page - 1}', 'size': f'{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)
|
|
|
|
|
|
class _PageData(BaseModel, Generic[DataT]):
|
|
page_data: DataT | None = None
|
|
|
|
|
|
async def paging_data(db: AsyncSession, select: Select, page_data_schema: SchemaT) -> dict:
|
|
"""
|
|
基于 SQLAlchemy 创建分页数据
|
|
|
|
:param db:
|
|
:param select:
|
|
:param page_data_schema:
|
|
:return:
|
|
"""
|
|
_paginate = await paginate(db, select)
|
|
page_data = _PageData[_Page[page_data_schema]](page_data=_paginate).model_dump()['page_data']
|
|
return page_data
|
|
|
|
|
|
# 分页依赖注入
|
|
DependsPagination = Depends(pagination_ctx(_Page))
|