惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
Recorded Future
Recorded Future
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The GitHub Blog
The GitHub Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Microsoft Azure Blog
Microsoft Azure Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
Y
Y Combinator Blog
N
News | PayPal Newsroom
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
博客园 - Franky
SecWiki News
SecWiki News
Recent Announcements
Recent Announcements
T
Troy Hunt's Blog
The Register - Security
The Register - Security
The Last Watchdog
The Last Watchdog
Webroot Blog
Webroot Blog
S
Security Affairs
博客园 - 司徒正美
S
Schneier on Security
I
InfoQ
博客园_首页
www.infosecurity-magazine.com
www.infosecurity-magazine.com
T
Threat Research - Cisco Blogs
Forbes - Security
Forbes - Security
腾讯CDC
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
Cloudbric
Cloudbric
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
A
About on SuperTechFans
Engineering at Meta
Engineering at Meta
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog
V
Vulnerabilities – Threatpost
C
Check Point Blog
Google DeepMind News
Google DeepMind News
Google Online Security Blog
Google Online Security Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cisco Blogs
Schneier on Security
Schneier on Security
O
OpenAI News
K
Kaspersky official blog

JieJiSS' Blog

Boot an Arch Linux RISC-V using qemu-system 关于基于机器学习的多目标对象追踪算法的文献综述 - JieJiSS' Blog RV64 板子更换 rootfs 指南 - JieJiSS' Blog ACTF2022 safer-tg-bot-{1,2} WP - JieJiSS' Blog A RISC-V gcc pitfall revealed by a glibc update Setup an Arch Linux RISC-V Development Environment 解构赋值踩坑:神秘失踪的数据 - JieJiSS' Blog How's the GDPR (used to) related to my blog Rust is incompatible with LLVM, at least partially python-mtrpacket 在 riscv64 上编译测试不通过 - JieJiSS' Blog 快速获得 RISC-V 开发环境 - JieJiSS' Blog 在 RISC-V 上编译 Node.js 16 Redmi AirDots 3 Pro 连接电脑后蓝牙类别不正确的解决方案 为什么 ArrowFunction 不能 new - JieJiSS' Blog Bilibili 2020-10-24 CTF WriteUp - JieJiSS' Blog 对于又一种对SS流式加密的攻击(奇虎团队)的详细分析 - JieJiSS' Blog 在MacOS上配置dnscrypt-proxy(免分流) - JieJiSS' Blog 市县中心点坐标(经纬度) - JieJiSS' Blog 二项式定理及其推广公式的一种简单理解办法 - JieJiSS' Blog
为 SQLAlchemy Model 添加 type hint 和 type check
2024-03-04 · via JieJiSS' Blog

文章目录

  1. TL;DR
  2. Motivation
  3. Implementation

TL;DR

Use from typing import dataclass_transform

Motivation

其实动机很简单,众所周知对于 SQLAlchemy 1.4 想要 typing 可以安装 sqlalchemy2-stub,对于 < 1.4 也可以有 sqlalchemy-stub。然而对于最新的 SQLAlchemy >= 2.0,因为它自己有类型注释,但是又很少,所以还没有一个很好的解决方案。本文就是为了介绍一种我摸索出的解决方案,基本上可以完美解决 SQLAlchemy Model / ORM 的 typing 问题。

当然,如果你在手写 raw sql,那肯定是没办法自动弄好类型的,不要做梦了🚫。本文只针对使用了 SQLAlchemy ORM Model 的用户。

Implementation

基本思路是,Python 在 PEP 681 (>= Python 3.11) 当中为 typing 模块提供了一个 dataclass_transform decorator,可以将第三方的 class 标注为和原生的 dataclass 提供类似的功能:

Most type checkers, linters and language servers have full support for dataclasses. This proposal aims to generalize this functionality and provide a way for third-party libraries to indicate that certain decorator functions, classes, and metaclasses provide behaviors similar to dataclasses.

These behaviors include:

  • Synthesizing an __init__ method based on declared data fields. <-- good for us
  • Optionally synthesizing __eq__, __ne__, __lt__, __le__, __gt__ and __ge__ methods.
  • Supporting “frozen” classes, a way to enforce immutability during static type checking.
  • Supporting “field specifiers”, which describe attributes of individual fields that a static type checker must be aware of, such as whether a default value is provided for the field. <-- mostly good for us

总体来讲这个 decorator 提供了所有我们想要的功能,唯一的问题是 field: type = mapped_column(...) 会导致 type checker 认为 fieldOptional 的。但总归基本的类型检查是能用的,甚至能兼容外键和 relationship,只是在初始化类的时候会有提供参数没写全的风险。如果感觉这样不够好,可以通过给每个 Model 编写一个 dummy __init__ 的方式来把 optional 干掉:

1
2
3
4
5
class A(Base):
id: MappedColumn[str] = mapped_column(String, nullable=False, ...)

def __init__(/, id: str):
...

实践上,我们为了方便,可以直接在 SQLAlchemy 的 Base class 上面使用这个 decorator,这样就不用每次定义一个 Model class 都要写一遍了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from sqlalchemy import Integer, String, Text, text, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedColumn, mapped_column, relationship

from typing import TYPE_CHECKING, dataclass_transform

if TYPE_CHECKING:

@dataclass_transform()
class Base(DeclarativeBase):
pass
else:

class Base(DeclarativeBase):
pass

class User(Base):
id: MappedColumn[str] = mapped_column(String, nullable=False, primary_key=True)
...

class Example(Base):
__tablename__ = "example"
id: MappedColumn[int] = mapped_column(Integer, autoincrement=True, primary_key=True)
user_id: MappedColumn[str] = mapped_column(String, ForeignKey("user.id"), nullable=False)

user: Mapped[User] = relationship(User, backref="example")

example = Example(...)
example.user.id

这里 class Base(DeclarativeBase): 写法得到的 Base class 和 Base = declarative_base() 得到的 Base 在功能上是一样的,但是给了我们使用 dataclass_transform 的空间。

来源:https://blog.jiejiss.com/