第 05 章 数据模型 Model
学习目标
学完本章,你应该能:
- 理解 SQLAlchemy 2.0 的现代写法(Mapped / mapped_column)
- 掌握 fba 中模型基类(Base)和 Mixin 的用法
- 能自己定义一个数据库模型
一、Model 是什么
Model(模型) 是数据库表的 Python 映射。通过 SQLAlchemy 这个 ORM 工具,你可以用 Python 类来表示一张数据库表:
- 一个类 = 一张表
- 类的属性 = 表的字段(列)
ORM:对象关系映射。让你用"操作 Python 对象"的方式操作数据库,底层自动翻译成 SQL,不必手写 SQL 语句。
类 ↔ 表的映射关系
二、SQLAlchemy 2.0 现代语法
fba 使用 SQLAlchemy 2.0 的最新写法,核心是两个关键词:
Mapped[...]:声明某个属性的类型(用于类型标注)mapped_column(...):声明这个属性对应数据库的一列,并设置列属性
基本示例
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import String
class User(Base):
__tablename__ = 'sys_user'
username: Mapped[str] = mapped_column(String(32), comment='用户名')
age: Mapped[int | None] = mapped_column(comment='年龄')解释:
Mapped[str]:告诉类型检查器,username是一个字符串mapped_column(String(32)):对应数据库里长度 32 的字符串列int | None:表示可空(允许 NULL)
三、通用模型基类
fba 的通用模型定义在 backend/common/model.py,这是理解 fba Model 的关键。
1. 主键(id_key)
fba 不提供自动主键,需要手动声明主键。它提供了一个 id_key 类型别名:
id_key = Annotated[
int,
mapped_column(
BigInteger,
primary_key=True, # 主键
unique=True, # 唯一
index=True, # 索引
autoincrement=True, # 自增
comment='主键 ID',
),
]使用时直接 id: Mapped[id_key] 即可。
Annotated:Python 类型标注的"别名",把类型 + 额外信息打包成一个可复用的名字。
2. Mixin 类(混入)
Mixin 是一种"把公共字段打包,让多个模型复用"的技术。fba 提供了几个常用 Mixin:
日期时间 Mixin(已集成在 Base 中):
class DateTimeMixin(MappedAsDataclass):
created_time: Mapped[datetime] = mapped_column(TimeZone, default_factory=timezone.now, comment='创建时间')
updated_time: Mapped[datetime | None] = mapped_column(TimeZone, onupdate=timezone.now, comment='更新时间')逻辑删除 Mixin(已集成在 Base 中):
class LogicalDeleteMixin(MappedAsDataclass):
deleted: Mapped[int] = mapped_column(default=0, comment='是否已删除(0:否;id:是)')
deleted_time: Mapped[datetime | None] = mapped_column(comment='删除时间')逻辑删除:不是真的删掉数据,而是打个标记(deleted=1),查询时自动过滤。好处是数据可恢复、可追溯。
3. 两个核心基类
# 数据类基类:带数据类集成,但不含日期时间和逻辑删除
class DataClassBase(MappedAsDataclass, MappedBase):
__abstract__ = True
# Base 基类:数据类 + 日期时间 + 逻辑删除,最常用
class Base(DataClassBase, DateTimeMixin, LogicalDeleteMixin):
__abstract__ = True
__abstract__ = True:表示这是"抽象基类",只用来继承,不会真的建一张表。
大部分业务模型直接继承 Base 即可,它会自动带上主键、创建时间、更新时间、逻辑删除标记。
基类继承关系图
四、主键模式:自增 ID vs 雪花 ID
fba 支持两种主键方案:
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 自增 ID | 数据库自动递增(1,2,3...) | 单机、简单项目 |
| 雪花 ID | 全局唯一的长整数 ID | 分布式、多实例场景 |
默认是自增 ID。切换雪花 ID 需要修改配置 DATABASE_PK_MODE,详见官方文档的"主键"章节。
雪花 ID(Snowflake):一种分布式全局唯一 ID 生成算法,生成的 ID 是很大的数字,能保证多台机器同时生成也不重复。
五、字符串类型规范
fba 对字符串长度有约定俗成的规范:
- 长文本:使用 fba 内置的
UniversalText(兼容 PostgreSQL 和 MySQL) - 常见文本:通常用
32、64、128、256、512这几个长度单位
例如用户名用 String(32),描述用 String(256),正文用 UniversalText。
六、完整示例:定义一个用户模型
综合以上知识,定义一个业务模型大概长这样:
from sqlalchemy import String
from sqlalchemy.orm import Mapped, mapped_column
from backend.common.model import Base, id_key
class User(Base):
"""用户表"""
__tablename__ = 'sys_user'
id: Mapped[id_key] = mapped_column(init=False) # 主键
username: Mapped[str] = mapped_column(String(32), unique=True, comment='用户名')
nickname: Mapped[str | None] = mapped_column(String(64), comment='昵称')
email: Mapped[str | None] = mapped_column(String(128), comment='邮箱')关键点:
- 继承
Base,自动获得 id、created_time、updated_time、deleted 等字段 __tablename__指定表名- 每个字段都写
comment,方便生成文档
七、常见疑问
Q1:为什么 fba 不提供自动主键? 为了灵活性——你可以自由选择自增 ID 或雪花 ID,且主键定义清晰可见,避免"黑盒"。
Q2:mapped_column(init=False) 是什么意思? 因为继承的是数据类基类(MappedAsDataclass),默认字段会出现在 __init__ 参数里。init=False 表示主键不在构造函数里显式传参(由数据库/算法生成)。
Q3:逻辑删除会影响查询吗? 会。查询时需要自动过滤 deleted=0 的记录,删除操作变成"打标记"而非真删。这一点在 CRUD 层需要配合处理。
本章小结
关键要点回顾:
- Model 用 SQLAlchemy 2.0 的
Mapped+mapped_column定义 - fba 通用模型在
backend/common/model.py - 主键手动声明,用
id_key别名 - 继承
Base自动获得时间戳 + 逻辑删除字段 - 字符串长度规范:32/64/128/256/512,长文本用 UniversalText
下一步: 模型定义好了,第 06 章我们学习如何用 Schema 校验进出接口的数据。
对应官方文档:
模型(backend/reference/model.html)