祥开龙角应三农,小队旌旗猎猎风。
models.py
class Group(Model):
id = IntField(pk=True)
name = CharField(max_length=10)
class User(Model):
id = IntField(pk=True)
group = ForeignKeyField('models.Group')
name = CharField(max_length=10)
utils.py
async def get_user_data(group_id: int, user_data: list[dict]) -> list[dict]:
return [dict(i, group=group_id) for i in user_data]
views.py
@router.get('')
async def user_pages(params: Params = Depends(), group_id: str = None):
user_data = await User.all().prefetch_related('group')
user_list = await get_user_data(group_id=group_id, user_data=user_data)
data = paginate(user_list, params=params)
return Response(status=0, data=data)
修改后的views.py
from fastapi_pagination.ext.tortoise import paginate
@router.get('')
async def user_pages(params: Params = Depends(), group_id: str = None):
queryset = User.all().prefetch_related('group') # 这里去掉await
async def transformer(items):
return await get_user_data(group_id=group_id, user_data=items)
data = await paginate(
queryset, params=params, transformer=transformer
)
return MyResponse(status=0, data=data)
- 示例2:(能修改get_user_data方法的情况)
import functools
# 把user_data放在第一个位置参数
async def get_user_data(user_data: list[dict], group_id, **kw) -> list[dict]:
return [dict(i, group_id=group_id) for i in user_data]
@router.get('')
async def user_pages(params: Params = Depends(), group_id: str = None):
queryset = User.all().prefetch_related('group')
transformer = functools.partial(get_user_data, group_id=group_id)
data = await paginate(
queryset, params=params, transformer=transformer
)
return MyResponse(status=0, data=data)
一个完整的示例:
#!/usr/bin/env python
from contextlib import asynccontextmanager
from functools import partial
from pathlib import Path
from typing import (
TYPE_CHECKING,
Annotated,
Any,
AsyncGenerator,
Optional,
)
import fastapi_cdn_host
import uvicorn
from faker import Faker
from fastapi import Depends, FastAPI
from fastapi_pagination import LimitOffsetPage, Page, Params, add_pagination
from fastapi_pagination.ext.tortoise import paginate
from pydantic import BaseModel, Field
from tortoise import fields, models
from tortoise.contrib.fastapi import RegisterTortoise
from tortoise.contrib.pydantic import PydanticModel, pydantic_model_creator
from tortoise.exceptions import DoesNotExist
faker = Faker()
class Group(models.Model):
id = fields.IntField(pk=True)
name = fields.TextField()
users: fields.ReverseRelation["User"]
class User(models.Model):
id = fields.IntField(pk=True)
name = fields.TextField(null=False)
email = fields.TextField(null=False)
group: fields.ForeignKeyRelation[Group] = fields.ForeignKeyField(
"models.Group", related_name="users"
)
if TYPE_CHECKING: # pragma: nocoverage
class UserIn(User, PydanticModel): # type:ignore[misc]
pass
class UserOut(User, PydanticModel): # type:ignore[misc]
pass
else:
UserOut = pydantic_model_creator(User, name="User")
class UserIn(pydantic_model_creator(User, name="UserIn", exclude_readonly=True)):
group_id: int = Field(gt=0)
async def _initial_users() -> None:
await Group.bulk_create([Group(name=faker.name()) for _ in range(10)])
gs = await Group.all()
for i in range(100):
await User.create(
name=faker.name(),
email=faker.email(),
group=gs[i % len(gs)],
)
class UserInfoSchema(BaseModel):
user_id: int
info: Annotated[str, "<name email>"]
group: str
async def build_user_info(items: list[UserOut], group_name="") -> list[UserInfoSchema]:
return [
UserInfoSchema(
user_id=i.id, info=f"<{i.name} {i.email}>", group=group_name or i.group.name
)
for i in items
]
async def get_user_data(group_id, items) -> list[UserInfoSchema]:
try:
g = await Group.get(id=group_id)
group_name = g.name
except (DoesNotExist, TypeError, ValueError):
group_name = ""
return await build_user_info(items, group_name)
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
async with RegisterTortoise(
app,
db_url="sqlite://:memory:",
modules={"models": [__name__]},
generate_schemas=True,
):
await _initial_users()
add_pagination(app)
yield
app = FastAPI(title="Tortoise ORM Pagination example", lifespan=lifespan)
fastapi_cdn_host.patch_docs(app)
@app.post("/users", response_model=UserOut)
async def create_user(user_in: UserIn) -> Any:
return await User.create(**user_in.dict())
@app.get("/users/default", response_model=Page[UserInfoSchema])
@app.get("/users/limit-offset", response_model=LimitOffsetPage[UserInfoSchema])
async def get_users() -> Any:
data = await paginate(
User.all().prefetch_related("group"),
transformer=partial(build_user_info, group_name=""),
)
# print(type(data.items))
# print(type(data.items[0]))
return data
class ResponseModel(BaseModel):
status: int
data: Page[UserInfoSchema]
msg: Optional[str] = ""
async def build_user_data(
user_data: list[UserOut], group_id: Optional[str] = None
) -> list[UserInfoSchema]:
return await get_user_data(group_id=group_id, items=user_data)
@app.get("/users/info", response_model=ResponseModel, summary="分页用户列表")
async def get_user_infos(
params: Params = Depends(), group_id: Optional[str] = None
) -> Any:
queryset = User.all().prefetch_related("group")
if group_id:
queryset = queryset.filter(group_id=group_id)
transformer = partial(build_user_data, group_id=group_id)
data = await paginate(queryset, params=params, transformer=transformer)
return ResponseModel(status=0, data=data)
class MyResponse:
def __init__(self, data: Any, status: int, msg="") -> None:
self.data = data
self.status = status
if msg:
self.msg = msg
def __str__(self):
return {"data": self.data, "status": self.status}
@app.get("/users/info2")
async def get_user_infos2(params: Params = Depends(), group_id: str = None):
queryset = User.all().prefetch_related("group")
async def transformer(user_data):
return await get_user_data(group_id=group_id, items=user_data)
data = await paginate(queryset, params=params, transformer=transformer)
return MyResponse(status=0, data=data, msg="bibi")
if __name__ == "__main__":
uvicorn.run(f"{Path(__file__).stem}:app")