Models 类定义中,Base类继承DeclarativeBase,MappedAsDataclass的好处

简介: Python 中 SQLAlchemy 这一开源库的使用方法

SQLAlchemy模型设计中的一些核心概念。这种设计(继承 MappedAsDataclassDeclarativeBase)是SQLAlchemy现代声明式模型的一种强大且类型安全的写法。

下面是一个例子:

from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData

POSTGRES_INDEXES_NAMING_CONVENTION:dict[str,str] = {
    "ix": "ix_%(column_0_label)s",
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(column_0_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s",
}

metadata = MetaData(naming_convention=POSTGRES_INDEXES_NAMING_CONVENTION)

db = SQLAlchemy(metadata=metadata)
--------------------------------------------------------------------------
from sqlalchemy.orm import DeclarativeBase,MappedAsDataclass
from models.engine import metadata

class TypeBase(MappedAsDataclass, DeclarativeBase):
    """
    This is for adding type, after all finished, rename to Base.
    """

    metadata = metadata
    
    
 class Account(UserMixin, TypeBase):
    __tablename__ = "accounts"
    __table_args__ = (sa.PrimaryKeyConstraint("id", name="account_pkey"), sa.Index("account_email_idx", "email"))

    id: Mapped[str] = mapped_column(StringUUID, server_default=sa.text("uuid_generate_v4()"), init=False)
    name: Mapped[str] = mapped_column(String(255))
    email: Mapped[str] = mapped_column(String(255))
    password: Mapped[str | None] = mapped_column(String(255), default=None)
    password_salt: Mapped[str | None] = mapped_column(String(255), default=None)
    avatar: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    interface_language: Mapped[str | None] = mapped_column(String(255), default=None)
    interface_theme: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    timezone: Mapped[str | None] = mapped_column(String(255), default=None)
    last_login_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    last_login_ip: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
    last_active_at: Mapped[datetime] = mapped_column(
        DateTime, server_default=func.current_timestamp(), nullable=False, init=False
    )
    status: Mapped[str] = mapped_column(
        String(16), server_default=sa.text("'active'::character varying"), default="active"
    )
    initialized_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, server_default=func.current_timestamp(), nullable=False, init=False
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, server_default=func.current_timestamp(), nullable=False, init=False, onupdate=func.current_timestamp()
    )

    role: TenantAccountRole | None = field(default=None, init=False)
    _current_tenant: "Tenant | None" = field(default=None, init=False)

为什么是 MappedAsDataclassDeclarativeBase

这个 TypeBase类被设计为项目中所有数据模型(如 Account)的总基类。它结合了两个父类的功能,为子类提供了强大的能力:

基类

核心作用

带来的好处

DeclarativeBase

数据库映射:这是SQLAlchemy声明式系统的核心。它负责将Python类与数据库表关联起来(通过__tablename__),并管理所有映射的列、关系等。

提供了ORM功能,让你能用Python对象操作数据库表。

MappedAsDataclass

数据类行为:这是一个混入类(Mixin),它让模型类具备类似Python dataclass的特性,如自动生成__init__方法。

简化了对象初始化,支持更清晰、更现代的类型注解(如Mapped[str]),使代码更易写、易读,且IDE支持更好。

简单来说,DeclarativeBase负责“数据库的事”,而 MappedAsDataclass负责“Python对象的事”。两者结合,创造出了一个既强大又易用的模型基类。


DeclarativeBase(SQLAlchemy 2.0)

作用:

  • 提供声明式 ORM 基类
  • 管理表结构、元数据、注册机制
  • Mapped 类型注解
# 1. 表注册和元数据管理
__tablename__ = "accounts"  # 定义表名
metadata = metadata  # 使用统一的元数据

# 2. ORM 查询能力
account = db.session.query(Account).filter_by(email="test@example.com").first()

# 3. 关系映射
# 可以定义 relationships, backrefs 等

# 4. 类型注解支持
id: Mapped[str]  # SQLAlchemy 2.0 的类型注解

如果不继承 DeclarativeBase:

无法使用声明式 ORM 需要手动创建 Table 对象

无法使用 Mapped 类型注解

失去 ORM 的便利性

MappedAsDataclass(SQLAlchemy 2.0)

作用:

  • 让模型像 Python dataclass 一样工作
  • 自动生成 __init____repr__ 等方法
  • 支持 field()init=False 等 dataclass 特性
# 1. 自动生成 __init__ 方法
account = Account(
    name="张三",
    email="zhangsan@example.com",
    password="hashed_password"
)
# 不需要手动写 __init__
# 2. 支持 dataclass 的 field() 函数
role: TenantAccountRole | None = field(default=None, init=False)
# init=False 表示这个字段不会出现在 __init__ 参数中
# 3. 支持 init=False 在 mapped_column 中
id: Mapped[str] = mapped_column(StringUUID, default=lambda: str(uuid4()), init=False)
# init=False 表示创建对象时不需要传入 id,会自动生成
# 4. 自动生成 __repr__ 等方法
print(account)  # 会有默认的字符串表示


🔍 Account 类如何利用父类特性

Account类继承 TypeBase后,其字段定义大量使用了由父类带来的能力:

1. 类型注解与列映射

name: Mapped[str] = mapped_column(String(255))

  • Mapped[str]:这是由 DeclarativeBase系统识别的类型注解,指明 name属性是映射到数据库的列,且类型为 str
  • mapped_column(String(255))mapped_column是 SQLAlchemy 2.0 的现代写法,替代了传统的 Column,用于定义列的细节(如数据类型 String(255))。


2. 数据类行为控制

  • init=False:这是 MappedAsDataclass提供的功能。表示该字段不包含在自动生成的 __init__构造函数参数中。例如 idcreated_at通常由数据库自动生成,所以创建 Account对象时不需要传入。
  • default=None:这也是数据类特性。为字段设置默认值。创建 Account对象时,如果没提供 password的值,它就会是 None


3. 数据库端默认值

last_active_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.current_timestamp(), init=False)

  • server_default:由 DeclarativeBase系统处理,指定在数据库层面的默认值(如 func.current_timestamp())。这与 Python 层面的 default是不同的。

⚖️ 对比:不继承父类的传统写法

如果不使用 TypeBase提供的现代组合功能,Account类的定义会变得冗长且缺乏类型提示。

下面是一个简化的对比示例,假设只定义 id, name, email三个字段:

# ✅ 现代写法(继承 TypeBase)
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, mapped_column
from sqlalchemy import String, func

class TypeBase(MappedAsDataclass, DeclarativeBase):
    pass

class Account(TypeBase):
    __tablename__ = "accounts"
    id: Mapped[str] = mapped_column(primary_key=True, init=False)
    name: Mapped[str] = mapped_column(String(255))
    email: Mapped[str] = mapped_column(String(255))

# 创建对象非常简洁,且有类型检查
new_account = Account(name="张三", email="zhangsan@example.com")

----------------------------------------------------------------------------------------------
# ❌ 传统写法(不继承这些父类)
from sqlalchemy import Column, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# 1. 需要手动创建基类
Base = declarative_base()

# 2. 类定义无法使用 Mapped 类型注解,列定义用 Column
class Account(Base):
    __tablename__ = "accounts"
    # 每个字段都需要用 Column 详细定义,代码冗长
    id = Column(String(255), primary_key=True)
    name = Column(String(255))
    email = Column(String(255))

    # 3. 如果想有初始化逻辑,需手动写 __init__
    def __init__(self, name, email):
        self.name = name
        self.email = email

    # 4. 没有方便的默认 __repr__ 等方法

# 创建对象
new_account = Account(name="张三", email="zhangsan@example.com")

💡 不继承 DeclarativeBase

如果不继承 DeclarativeBase,你将无法使用SQLAlchemy的声明式模型(即通过类定义表结构这种最常用的方式),而必须回归到更底层的命令式映射(Imperative Mapping 或 Classical Mapping)。

这意味着你需要:

  1. 先手动定义一个 Table对象来描述表结构。
  2. 再定义一个普通的Python类。
  3. 最后使用 mapper()函数手动地将两者关联起来
# ✅ 现代写法:继承 DeclarativeBase
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class Base(DeclarativeBase):
    pass

class User(Base):  # 继承DeclarativeBase
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column()
       
--------------------------------------------------------------------------------------
    
# ❌ 传统写法:不继承 DeclarativeBase (命令式映射)
from sqlalchemy import Table, Column, Integer, String, MetaData
from sqlalchemy.orm import registry

# 1. 手动创建注册表
mapper_registry = registry()
metadata = MetaData()

# 2. 先像定义普通SQL表一样,用Table构造表
user_table = Table(
    "users",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String(30)),
)

# 3. 定义一个普通的Python类
class User:
    def __init__(self, name: str):
        self.name = name

# 4. 使用注册表的 map_imperatively 方法手动将类和表关联起来
mapper_registry.map_imperatively(User, user_table)

主要影响:代码变得冗长且不直观,失去了声明式模型的简洁性和可读性。声明式模型将所有信息集中在类定义中,一目了然


💡 不继承 MappedAsDataclass

MappedAsDataclass是一个混入类,它为你提供了类似Python dataclass的便利特性。如果不继承它,你将无法使用SQLAlchemy 2.0风格的现代类型注解,也无法自动获得一些便利方法。

# ✅ 现代写法:继承 MappedAsDataclass
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, mapped_column
class Base(MappedAsDataclass, DeclarativeBase):
    pass
class User(Base):  # 继承的Base包含了MappedAsDataclass
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True, init=False)  # 使用Mapped注解,init=False控制构造函数
    name: Mapped[str] = mapped_column()
    email: Mapped[str | None] = mapped_column(default=None)  # 支持默认值
# 创建对象非常简洁,且有类型检查和自动生成的__init__方法
new_user = User(name="张三")  # 只需传name,email使用默认值None


# ❌ 传统写法:不继承 MappedAsDataclass (仅继承DeclarativeBase)
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
    pass
class User(Base):  # 仅继承DeclarativeBase
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)  # 不使用Mapped注解
    name = Column(String(30))
    email = Column(String(255), default=None)  # 数据库层面的默认值
    # 可能需要手动编写__init__方法以获得灵活的构造函数
    def __init__(self, name: str, email: str | None = None):
        self.name = name
        self.email = email
# 创建对象
new_user = User(name="李四")

主要影响

  1. 失去现代类型注解:无法使用 Mapped[类型]这种清晰的注解方式,代码的类型提示作用减弱,IDE的智能提示和支持也会变差。
  2. 失去数据类特性:不会自动生成一个智能的 __init__构造函数。你需要手动编写 __init__方法才能实现类似 init=False(某些字段不放入构造函数)或灵活的默认值逻辑


总结

可以这样理解这个设计:

  • TypeBase:是项目自产的、功能强大的 “模型零件”,它融合了数据库映射和现代化数据类两种特性。
  • Account:作为具体的 “产品”(用户账户模型),使用统一的“零件”是天经地义、也是最可靠的选择。


目录
相关文章
|
11天前
|
机器学习/深度学习 自然语言处理 算法
从贝叶斯视角解读Transformer的内部几何:mHC的流形约束与大模型训练稳定性
大模型训练常因架构改动破坏内部贝叶斯几何结构,导致不稳定。研究表明,Transformer通过残差流、注意力与值表征在低维流形上实现类贝叶斯推理。mHC通过约束超连接保护这一几何结构,确保规模化下的训练稳定与推理一致性。
220 7
从贝叶斯视角解读Transformer的内部几何:mHC的流形约束与大模型训练稳定性
|
20天前
|
数据可视化 安全 测试技术
Anthropic 开源 Bloom:基于 LLM 的自动化行为评估框架
Anthropic推出开源框架Bloom,可自动化评估大语言模型是否阿谀奉承、有政治倾向或绕过监管等行为。不同于传统基准,Bloom基于配置动态生成测试场景,支持多模型、多样化评估,并提供可视化分析,助力模型安全与对齐研究。(237字)
110 12
Anthropic 开源 Bloom:基于 LLM 的自动化行为评估框架
|
25天前
《KOL/KOC与买量投放的深度融合优化指南》
本文聚焦KOL/KOC营销与传统买量投放的深度融合优化路径。针对品牌两种营销模式割裂导致的转化成本高、种草效果流失等痛点,提出构建以用户全生命周期价值为核心的一体化评估体系,通过多触点归因模型合理分配渠道价值。同时,围绕预算动态分配、内容资产复用、投放策略联动阐述成本控制技巧,结合品牌冷启动、稳定增长、成熟等不同阶段给出场景化组合方案,强调以数据驱动形成优化闭环。
221 42
|
17天前
|
机器学习/深度学习 人工智能 缓存
CALM自编码器:用连续向量替代离散token,生成效率提升4倍
近年来语言模型效率优化多聚焦参数规模与注意力机制,却忽视了自回归生成本身的高成本。CALM提出新思路:在token之上构建潜在空间,通过变分自编码器将多个token压缩为一个连续向量,实现“一次前向传播生成多个token”。该方法大幅减少计算次数,提升推理速度与吞吐量,同时引入无似然训练与BrierLM评估体系,突破传统语言建模范式,为高效大模型提供新路径。
98 7
CALM自编码器:用连续向量替代离散token,生成效率提升4倍
|
12天前
|
Web App开发 人工智能 运维
2025年主流Web自动化测试工具功能与适用场景对比
文章围绕2025年主流Web自动化测试工具展开,介绍行业发展趋势与痛点,对比优测、Selenium等工具的功能、优势、劣势及适用场景。指出不同工具呈差异化路径,企业应依团队技术、业务需求和预算选适配方案,还解答了工具选择、协同使用等常见问题。
|
1月前
|
SQL 人工智能 数据库
你的数据库不是性能差,是你的SQL在“烧钱”:用这条指令让AI化身资深DBA
硬件升配解决不了烂SQL!本文提供一套经过验证的AI指令,将大模型转化为资深DBA,通过深度诊断、索引优化和执行计划分析,帮助开发者从根源解决慢查询问题,实现数据库性能的降本增效。
212 19
|
13天前
|
人工智能 自然语言处理 安全
Lux 上手指南:让 AI 直接操作你的电脑
Lux 是一款能直接操作计算机的AI基础模型,通过视觉理解与动作预测,实现自然语言指令下的自动化任务。它无需依赖API,可像真人一样点击、输入、滚动,完成浏览器操作等复杂工作,准确率超越主流模型,是迈向“意图即执行”的重要突破。(238字)
145 13
Lux 上手指南:让 AI 直接操作你的电脑
|
7天前
|
人工智能 测试技术 API
一线工程师 2025 总结:LLM 只用了不到 10%,剩下 90% 卡在哪?
2025年,LLM能力爆发,但多数企业仅用到其10%。真正瓶颈不在模型强弱,而在工程落地:延迟不可控、并发崩溃、换模成本高、成本失控成常态。当LLM从“工具”变为“基础设施”,中转层与系统稳定性成为关键。释放剩余90%潜力,需扎实的架构设计与工程治理。
|
19天前
|
测试技术 API C#
一个 .NET 开源免费、功能强大的 UI 自动化库
FlaUI 是一个 .NET 平台开源免费的 UI 自动化测试库,基于 Microsoft UI Automation,支持 Win32、WPF、WinForms 等桌面应用自动化,提供灵活 API,助力高效编写自动化脚本,适用于功能、回归及 UI 验证测试。
|
26天前
|
数据采集 算法 机器人
具身智能:零基础入门睿尔曼机械臂(五)—— 手眼标定核心原理与数学求解
本文系统讲解手眼标定技术,涵盖Eye-in-Hand与Eye-to-Hand两种架构,深入推导AX=XB方程的数学原理与求解方法,结合实际应用场景和操作步骤,为机器人视觉开发者提供从理论到实践的完整指南。
248 9