为 SQLAlchemy Model 添加 type hint 和 type check

💡 原文中文,约2800字,阅读约需7分钟。
📝

内容提要

本文介绍了一种解决SQLAlchemy Model/ORM的typing问题的解决方案。通过使用Python的PEP 681中的dataclass_transform装饰器,可以将第三方类标注为类似于dataclass的功能。这个装饰器提供了大部分所需的功能,但是会导致类型检查器认为字段是可选的。可以通过编写一个dummy __init__方法来解决这个问题。可以直接在SQLAlchemy的Base类上使用这个装饰器,以避免重复编写Model类。

🔎

延伸解读

适用版本与前置条件

本文方案主要针对 SQLAlchemy 2.0 及以上版本,因为该版本自带类型注释但不够完善。对于 1.4 及以下版本,可分别使用 sqlalchemy2-stub 或 sqlalchemy-stub。此外,dataclass_transform 装饰器需要 Python 3.11 及以上版本(PEP 681),因此使用前需确认环境满足这些条件。

字段可选性问题的处理

使用 dataclass_transform 后,类型检查器会将所有字段视为可选,因为 mapped_column 的默认值可能为 None。这可能导致初始化时漏传参数而不报错。文章建议为每个模型编写一个 dummy __init__ 方法,显式声明必需参数,从而消除可选性,确保类型检查器能捕获缺失的初始化参数。

在 Base 类上统一应用

为了避免在每个模型类上重复添加装饰器,可以直接在 SQLAlchemy 的 Base 类上使用 dataclass_transform。这样所有继承 Base 的模型都会自动获得类似 dataclass 的类型检查行为,包括自动生成 __init__ 方法、比较方法等,简化了代码并保持一致性。

❓

Q&A

SQLAlchemy 2.0 中如何为 ORM Model 添加类型提示?

可以使用 PEP 681 的 dataclass_transform 装饰器,将其应用于 SQLAlchemy 的 Base 类,从而让类型检查器将 Model 类视为类似 dataclass,自动生成 __init__ 方法等。

为什么使用 dataclass_transform 后字段会被认为是可选的?如何解决?

因为 mapped_column 赋值会导致类型检查器认为字段是 Optional 的。可以通过为每个 Model 编写一个 dummy __init__ 方法来明确参数类型,从而消除可选性。

SQLAlchemy 1.4 及以下版本如何获得类型支持?

对于 SQLAlchemy 1.4,可以安装 sqlalchemy2-stub;对于低于 1.4 的版本,可以使用 sqlalchemy-stub。

dataclass_transform 装饰器提供了哪些功能?

它支持自动生成 __init__ 方法、比较方法(如 __eq__、__lt__ 等)、字段属性描述(如默认值),以及支持 frozen 类等,类似于原生 dataclass 的行为。

是否可以直接在 SQLAlchemy 的 Base 类上使用 dataclass_transform?

是的,可以直接在 Base 类上使用该装饰器,这样所有继承 Base 的 Model 类都会自动获得类似 dataclass 的类型检查支持,无需重复编写。

手写原始 SQL 能否自动获得类型检查?

不能,手写原始 SQL 无法自动处理类型问题。本文的方案仅针对使用 SQLAlchemy ORM Model 的用户。

🏷️

标签

➡️

继续阅读