SQLAlchemy 2.0 类型注解指南:`Mapped` 与 `mapped_column`

简介

在 SQLAlchemy 1.4 和 2.0 中,ORM(对象关系映射)引入了一种新的声明式映射系统,核心组件是 Mapped 类型注解和 mapped_column 构造函数。这种新风格旨在提供更好的 Python 类型提示(Type Hinting)支持,解决旧版 Column 写法在静态代码分析(如 Pyright, MyPy)和 IDE 自动补全方面的问题。

解决的问题

1. 静态类型检查报错

这是最常见的问题。在旧版写法中,模型属性被定义为 Column 对象,例如:

python 复制代码
id = Column(Integer, primary_key=True)

对于静态分析工具(如 Pyright),id 的类型是 Column[Integer]。然而,当你实例化模型对象访问 user.id 时,其实际值是 int 类型。

当你尝试将 user.id 传递给一个期望 int 的函数时,Pyright 会报错:

Argument of type "ColumnInteger" cannot be assigned to parameter of type "int"

Mapped 解决了这个问题。它明确告诉类型检查器,虽然我们在类定义中使用了描述符,但在实例中该属性表现为指定的 Python 类型。

2. IDE 智能感知

由于类型明确,现代 IDE(如 VS Code, PyCharm)能更准确地提供代码补全和错误提示。例如,定义为 Mapped[str | None] 的字段,IDE 会提示你该字段可能为 None。

用法对比

1. 基本字段

旧写法 (Legacy):

python 复制代码
from sqlalchemy import Column, Integer, String

class User(Base):
    __tablename__ = "user"
    
    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)
    email = Column(String(100))

新写法 (Modern - SQLAlchemy 2.0+):

python 复制代码
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import String
from typing import Optional

class User(Base):
    __tablename__ = "user"
    
    # Mapped[int] 告诉类型检查器:实例中的 id 是 int 类型
    # mapped_column(...) 定义了数据库列的属性
    id: Mapped[int] = mapped_column(primary_key=True)
    
    # nullable=False 是默认的,对应 Mapped[str]
    name: Mapped[str] = mapped_column(String(50))
    
    # Optional[str] 或 str | None 对应 nullable=True
    email: Mapped[Optional[str]] = mapped_column(String(100))

2. 复杂类型 (UUID, DateTime)

旧写法:

python 复制代码
import uuid
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import Column, DateTime, func

class Document(Base):
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    created_at = Column(DateTime(timezone=True), server_default=func.now())

新写法:

python 复制代码
import uuid
from datetime import datetime
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import DateTime, func

class Document(Base):
    # 明确指定 id 是 uuid.UUID 类型
    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    
    # 明确指定 created_at 是 datetime 类型
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

3. 外键与关系

旧写法:

python 复制代码
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

class Post(Base):
    user_id = Column(Integer, ForeignKey("user.id"))
    user = relationship("User")

新写法:

python 复制代码
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship

class Post(Base):
    user_id: Mapped[int] = mapped_column(ForeignKey("user.id"))
    
    # 这里的 relationship 也支持 Mapped 类型
    user: Mapped["User"] = relationship()

关键点总结

  1. 导入 : 使用 from sqlalchemy.orm import Mapped, mapped_column。
  2. 类型注解 : 必须为每个列添加 Python 类型注解(如 : Mapped[int])。
  3. Nullable :
    • Mapped[str] 隐含 nullable=False。
    • Mapped[Optional[str]] 或 Mapped[str | None] 隐含 nullable=True。
  4. SQL 类型 : 在 mapped_column() 中通过第一个参数指定 SQL 类型(如 String(50)),如果类型可以从 Python 类型推断(如 int -> Integer),则可以省略。但对于 String(需要长度)或 UUID 等特殊类型,通常还是需要指定。

通过采用这种新写法,你的代码将更加健壮,更易于维护,并且能通过严格的静态类型检查。

相关推荐
benchmark_cc12 分钟前
量化开发中,什么时候应该停止手动下载股票数据?
python·数据分析·pandas·量化交易·1024程序员节·股票数据·quantdash
泡茶喝茶写代码21 分钟前
A股量化策略回测实战:从行情数据到绩效评估(第 15 篇):新高新低策略
python·1024程序员节·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
全栈练习生38 分钟前
Jev 决策模型
python·ai·1024程序员节
LMT流氓兔38 分钟前
第五单元——第十二课:把脚本整理成项目
java·数据库·python·langchain·1024程序员节
光依旧39 分钟前
day-trader-skill实测:让Claude Code当交易助理,纯Python+确定性+fail-closed
python·量化交易·ai agent·a股·风控·claude code·开源项目实测
CCblandy2 小时前
前缀缓存命中率不靠引擎靠排列:本机仿真 3 轮,易变字段放开头 98.0%→0.0%
人工智能·python·算法·自然语言处理·性能优化
Sakura_j_h_2 小时前
给 FastAPI 接大模型:超时、重试和 token 成本该怎么管
python·大模型·fastapi·超时重试·token计费
forest_092 小时前
用Python复刻ClaudeDuet:主动式Agent四件套,定时晨报、后台巡检、危险动作审批(附完整可运行代码)
人工智能·python·架构·prompt·agi
小小张说故事2 小时前
Python 的函数调用到底贵在哪?拆开一次 call:51 纳秒栈帧与 *args 的 40 纳秒罚单
python
程序猿乐锅3 小时前
【从0-1学习Python | 第七篇】Python 面向对象基础
开发语言·笔记·后端·python·学习·1024程序员节