68 lines
1.9 KiB
Python
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)
|