SQLALCHEMY中DeclarativeBase,MappedAsDataclass

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

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

场景:

比如我们在flask中想要写一个Base 类去继承 MappedAsDataclass, DeclarativeBase,然后让其它的类去继承Base类:

下面是基础类的定义

from sqlalchemy.orm import DeclarativeBase,MappedAsDataclass

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

   pass


为什么继承 MappedAsDataclassDeclarativeBase

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

基类

核心作用

带来的好处

DeclarativeBase

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

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

MappedAsDataclass

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

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

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

关系总结

组件 作用 与 mapped_column 的关系
DeclarativeBase 提供 ORM 基础设施 扫描和处理 mapped_column 返回的对象
MappedAsDataclass 提供 dataclass 功能 与 mapped_columninit 参数配合
mapped_column 创建列定义对 返回 MappedColumn,被 DeclarativeBase 处理
Registry 管理所有映射类 存储 Account 的映射信息
MetaData 管理数据库模式 存储 Account 的表定义


继承DeclarativeBase 的完整调用解析

执行 Python 文件(account.py)在下面的举例中有提到Account 类的定义继承了TypeBase类
    
from models.base import TypeBase
    
1. 加载 models/base.py 模块
    
2. 执行 base.py 顶层代码(按顺序)
    
   a. from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass, ...
    
   b. 加载 sqlalchemy.orm 模块
      
      ┌─────────────────────────────────────
        sqlalchemy.orm 模块中:           
                                           
       1. 定义元类 DeclarativeAttributeIntercept
                                           
       2. 定义 DeclarativeBase           
          class DeclarativeBase(          
              metaclass=DeclarativeAttributeIntercept 
          ):                              
              # 类体执行                  
              # __init_subclass__ 被定义  
                                           
       3. 定义 MappedAsDataclass        
                                           
       4. 定义其他类(Mapped, mapped_column等) 
      └─────────────────────────────────────
    
   c. 回到 base.py 继续执行
      metadata = sa.MetaData()
    
   d. 定义 TypeBase 
      class TypeBase(MappedAsDataclass, DeclarativeBase):
          metadata = metadata
      
      执行 TypeBase 类体:metadata = metadata
      
      元类处理 TypeBase 类(DeclarativeAttributeIntercept)
      
      调用 __init_subclass__ 
      
      MappedAsDataclass.__init_subclass__
      
      DeclarativeBase.__init_subclass__
        (TypeBase 直接继承,执行 _setup_declarative_base)
      
      TypeBase 类定义完成,拥有 registry  metadata
    
3. TypeBase 导入完成,回到 account.py
    
4. 继续执行 account.py 的其他导入
    from sqlalchemy.orm import Mapped, mapped_column
    from sqlalchemy import String
    
5. 定义 Account 
    class Account(UserMixin, TypeBase):
        __tablename__ = "accounts"
        id: Mapped[str] = mapped_column(StringUUID, ...)
        name: Mapped[str] = mapped_column(String(255))
        email: Mapped[str] = mapped_column(String(255))
        password: Mapped[str | None] = mapped_column(...)
        password_salt: Mapped[str | None] = mapped_column(...)
        avatar: Mapped[str] = mapped_column(...)
    
6. 执行 Account 类体(按顺序执行字段定义)
    
7. 元类处理 Account 类(同样使用 DeclarativeAttributeIntercept)
    
8. 调用 __init_subclass__ 
    
    MappedAsDataclass.__init_subclass__(Account)
    
    DeclarativeBase.__init_subclass__(Account)
        (Account 间接继承,执行 _as_declarative)
        
        扫描 Account.__dict__,创建 Table  Mapper
        
        Account 类定义完成,拥有 __table__  __mapper__
        
9. Account 可以使用 ORM 功能


🔍 Account 类继承详细解释

下面是在Flask框架中使用Sqlalchemy定义的Account 类.

#engine.py
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)
--------------------------------------------------------------------------
#base.py
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
    
--------------------------------------------------------------------------   
 #Account.py
    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)



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

如果不使用 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:作为具体的 “产品”(用户账户模型),使用统一的“零件”是天经地义、也是最可靠的选择。


目录
相关文章
|
2月前
|
存储 自然语言处理 数据库
RAG(检索增强生成)技术简介
RAG(检索增强生成)通过结合信息检索与文本生成,提升大模型回答的准确性与时效性。它利用向量数据库实现语义检索,支持智能客服、医疗诊断、法律咨询等场景,解决知识更新难、专业性不足等问题,并以“按需取用”机制突破上下文限制,兼顾效率与隐私。
401 4
|
2月前
|
数据采集 监控 数据可视化
快速上手:LangChain + AgentRun 浏览器沙箱极简集成指南
AgentRun Browser Sandbox 是基于云原生函数计算的浏览器沙箱服务,为 AI Agent 提供安全、免运维的浏览器环境。通过 Serverless 架构与 CDP 协议支持,实现网页抓取、自动化操作等能力,并结合 VNC 实时可视化,助力大模型“上网”交互。
612 43
|
2月前
|
Kubernetes 应用服务中间件 API
应对 Nginx Ingress 退役,是时候理清这些易混淆的概念了
本文希望提供一种更简单的方式,来理解这些容易混淆的技术概念:Nginx、Ingress、Ingress Controller、Ingress API、Nginx Ingress、Higress、Gateway API。
988 84
|
2月前
|
人工智能 测试技术 开发者
AI Coding后端开发实战:解锁AI辅助编程新范式
本文系统阐述了AI时代开发者如何高效协作AI Coding工具,强调破除认知误区、构建个人上下文管理体系,并精准判断AI输出质量。通过实战流程与案例,助力开发者实现从编码到架构思维的跃迁,成为人机协同的“超级开发者”。
1877 106
|
2月前
|
Python
Python基础 - 类中__init_subclass_方法执行顺序
本文详解Python类与实例化时的执行顺序:类定义时先执行类体,再调用`__init_subclass__`;实例化时先调用`__new__`创建对象,再调用`__init__`初始化。`__init_subclass__`仅在类定义时执行一次,确保父类可访问子类属性,如SQLAlchemy中表结构的构建原理。
93 9
|
2月前
|
存储 关系型数据库 数据库
Flask中定义类使用Sqlalchemy时,mapped_column详细讲解
本文详解 SQLAlchemy 2.0 中 `mapped_column` 的用法,介绍其语法结构、常用参数(如 `nullable`、`default`、`server_default`、`onupdate` 等),并通过 DIFY 的 Account 模型实例解析字段定义逻辑,对比数据库列与运行时字段的区别,帮助理解 ORM 映射机制。
171 3
|
4月前
|
机器学习/深度学习 人工智能 JSON
构建AI智能体:二十八、大语言模型BERT:原理、应用结合日常场景实践全面解析
BERT是谷歌2018年推出的革命性自然语言处理模型,采用Transformer编码器架构和预训练-微调范式。其核心创新在于双向上下文理解和掩码语言建模,能有效处理一词多义和复杂语义关系。BERT通过多层自注意力机制构建深度表示,输入融合词嵌入、位置嵌入和段落嵌入,输出包含丰富上下文信息的向量。主要应用包括文本分类、命名实体识别、问答系统等,在搜索优化、智能客服、内容推荐等领域发挥重要作用。
1940 10
|
安全 测试技术 Python
Python类中的Setter与Getter:跨文件调用的艺术
Python类中的Setter与Getter:跨文件调用的艺术
256 3
|
SQL 存储 Go
【译】SQLAlchemy文档:SQLAlchemy 统一教程
【译】SQLAlchemy文档:SQLAlchemy 统一教程
512 1
|
人工智能 开发者
基于LangChain的Prompt模板
LangChain是一个开源库,简化了基于LLM的AI应用开发,充当AI开发的万能适配器,抽象并整合了大语言模型(如OpenAI和文心)的交互。要使用LangChain,首先通过`pip install langchain`安装。示例展示了如何使用LangChain与OpenAI模型交互,包括直接调用OpenAI接口和使用LangChain接口。LangChain的提示词模板功能用于构建Prompt,提高与AI对话的效率。LangChainHub是一个资源库,提供模板、工作流和最佳实践,方便开发者发现和分享。本文介绍了LangChain的基本用法和其生态系统中的LangChainHub。
基于LangChain的Prompt模板

热门文章

最新文章