代码提交
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
"""ORM 基类与公共 Mixin。
|
||||
|
||||
约定:
|
||||
* 主键统一 id 自增
|
||||
* 所有表都带 created_at / updated_at
|
||||
* 业务表一律逻辑删除:is_del = 0 表示正常,1 表示已删除
|
||||
* 查询正常数据统一写 `Model.alive()`,禁止在 DAO 里手写 `is_del == 1`
|
||||
(上一版项目就是把方向写反导致了"查不到刚建的数据")
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import DateTime, SmallInteger
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
|
||||
DEL_FLAG_NORMAL: int = 0
|
||||
DEL_FLAG_DELETED: int = 1
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
"""SQLAlchemy 2.0 DeclarativeBase。"""
|
||||
|
||||
def to_dict(self, exclude: set[str] | None = None) -> dict[str, Any]:
|
||||
exclude = exclude or set()
|
||||
return {
|
||||
c.name: getattr(self, c.name)
|
||||
for c in self.__table__.columns
|
||||
if c.name not in exclude
|
||||
}
|
||||
|
||||
def __repr__(self) -> str: # pragma: no cover - 调试用
|
||||
pk = getattr(self, "id", None)
|
||||
return f"<{self.__class__.__name__} id={pk}>"
|
||||
|
||||
|
||||
class TimestampMixin:
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, default=datetime.now, nullable=False, comment="创建时间"
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, default=datetime.now, onupdate=datetime.now, nullable=False, comment="更新时间"
|
||||
)
|
||||
|
||||
|
||||
class SoftDeleteMixin:
|
||||
is_del: Mapped[int] = mapped_column(
|
||||
SmallInteger,
|
||||
default=DEL_FLAG_NORMAL,
|
||||
server_default="0",
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="逻辑删除:0=正常 1=已删除",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def alive(cls):
|
||||
"""正常数据的过滤条件,供 select().where() 使用。"""
|
||||
return cls.is_del == DEL_FLAG_NORMAL
|
||||
|
||||
def soft_delete(self) -> None:
|
||||
self.is_del = DEL_FLAG_DELETED
|
||||
Reference in New Issue
Block a user