Files
test/app/schema/common.py
T
2026-09-21 19:03:31 +08:00

68 lines
1.9 KiB
Python

"""Pydantic 公共模型与校验小工具。"""
from __future__ import annotations
from datetime import date, datetime
from enum import Enum
from typing import Any, Generic, TypeVar
from pydantic import BaseModel, ConfigDict, Field, field_validator
from app.model.constants import gender_to_code
T = TypeVar("T")
class ORMBase(BaseModel):
"""所有出参模型的基类:允许直接吃 ORM 对象(含 @property 派生字段)。"""
model_config = ConfigDict(from_attributes=True, populate_by_name=True)
class PageResult(BaseModel, Generic[T]):
total: int = Field(0, description="总条数")
page: int = Field(1, description="当前页")
page_size: int = Field(10, description="每页条数")
pages: int = Field(0, description="总页数")
items: list[T] = Field(default_factory=list, description="数据列表")
class SortOrder(str, Enum):
ASC = "asc"
DESC = "desc"
class DateRangeMixin(BaseModel):
start_date: str | None = Field(None, description="起始日期 YYYY-MM-DD")
end_date: str | None = Field(None, description="结束日期 YYYY-MM-DD")
# ---------------------------------------------------------------- 校验器
def _normalize_gender(cls, v): # noqa: ANN001, ANN201
if v is None:
return v
code = gender_to_code(v)
if code is None:
raise ValueError("性别只能是 1/2 或 男/女")
return code
def _strip_str(cls, v): # noqa: ANN001, ANN201
return v.strip() if isinstance(v, str) else v
GENDER_VALIDATOR = field_validator("gender", mode="before", check_fields=False)(_normalize_gender)
STRIP_VALIDATOR = field_validator("*", mode="before", check_fields=False)(_strip_str)
def date_to_str(value: date | datetime | None) -> str | None:
if value is None:
return None
if isinstance(value, datetime):
return value.date().isoformat()
return value.isoformat()
def as_dict(model: Any) -> dict:
return model.model_dump(exclude_unset=True)