SQLAlchemy
SQLAlchemy是 Python 下的 SQL Toolkit and Object Relational Mapper
框架。
安装
SQLAlchemy 2.0 支持 Python3.7 及以上版本:
SQLAlchemy 底层由特定数据库实现了 Python Database API Specification v2.0
规范的驱动来提供支持,因此还需要安装对应的数据库驱动,下面是常用的数据库的一些驱动:
- psycopg2: SQLAlchemy 默认的 PostgreSQL 驱动
- sqlite3: Python 标准库提供的 SQLite 驱动,同样是 SQLAlchemy 默认的 SQLite 驱动
- PyMySQL: 用于 MySQL/MariaDB 数据库
- duckdb-engine: SQLAlchemy 的 DuckDB 驱动
Tips
更多的可以查看的Dialects,其中还包含一些第三方提供的驱动。
架构
SQLAlchemy 有三种不同的 API 来进行操作,可以单独使用或组合在一起使用。其中 SQLAlchemy ORM
是构建在 SQLAlchemy Core
之上的,而 SQLAlchemy Core
又是构建在 DBAPI 之上的。他们由以下组件构成:
DBAPI
他由实现了 Python Database API Specification v2.0
规范的三方库提供,是真正与数据库之间的通信。
Tips
一些特殊的功能通过sqlalchemy.dialects.[driver]暴露出来。
Tips
SQLAlchemy Core
中的 Engine 是对这些驱动的进一步封装,Engine 管理由驱动提供的 Connection 对象并提供了连接池的功能。
SQLAlchemy Core
SQLAlchemy Core
是 SQLAlchemy 的基础架构。提供了用于管理数据库连接、构造 SQL 语句以及与数据库查询结果进行交互的工具:
- Schema: 定义数据库架构,通过指定元数据来构造数据库中表、列等架构实例
- SQL Expression Language: Core 的核心,用于构造 SQL 语句
- Engine: 任何 SQLAlchemy 应用程序的起点,管理数据库连接。底层由 DBAPI 来与数据库进行通信
他们之间是相辅相成的关系,SQL Expression Language
是其中的核心,他使用 Schema 定义来构造 SQL 语句,最终构造的 SQL 语句自动转换为字符串形式传递给 Engine 并由他管的连接来执行。
SQLAlchemy ORM
SQLAlchemy ORM 是构建在 SQLAlchemy Core
之上的,他提供了Mapped Class来使用 Python 中的类来定义数据库模式。并且提供了Session对象持久化机制来操作数据库。
Note
Core 和 SQL Expression Language
提供了以 Schema 为中心的数据库视图以及面向命令(不变性)的编程范式。ORM 在此基础上构建了以 domain 为中心的数据库视图以及面向对象并依赖可变性(状态的变化)的编程范式。
总结
SQLAlchemy 是 Python 下的 ORM 框架,提供了三种操作流程:
- DBAPI 流程: 创建 Connection → 使用
Connection.cursor()
返回 Cursor → 编写 SQL 字符串 → 执行cursor.execute(sql_string)
-> 返回结果 -> 关闭 Connection - Core 流程: 编写Schema → 构建SQL Expression Language → 从Engine中获取 Connection → 执行
Connection.execute(sql)
→ 返回Results → 操作完毕释放 Connection - ORM 流程: 编写Mapped Class → 操作Session → 自动转换为 Core 流程并且更新 Session 状态
可以看到 ORM 的流程要简单的多,我们只需要编写好 Mapped Class
然后操作 Session 即可,之后的流程大部分都是自动完成的。但是 ORM 流程是构建在 Core 之上的,实际上编写 Mapped Class
就是将编写 Schema 换一种写法(ORM 以类构建映射,Core 以方法创建 Schema 对象)。
而 Core 是构建在 DBAPI 之上的,她最核心的地方就是提供了 SQL Expression Language
来避免用户直接手写 SQL 字符串:
# DBAPI
# 创建 Connection 对象
connection = connect(URI)
# 获取 Curosr 对象
cursor = connection.cursor()
# 执行 SQL 语句
result = cursor.execute("SELECT name,fullname WHERE id=1")
connection.close()
# Core
from sqlalchemy import MetaData,create_engine
from sqlalchemy import Table, Column, Integer, String
# SQLAlchemy 应用的起点
engine = create_engine(URI)
# MetaData 即 数据库
metadata_obj = MetaData()
# 通过 Table 方法来构造表对象
# 通过 Column 方法来构造列对象
user = Table(
"user",
metadata_obj,
Column("id", Integer, primary_key=True),
Column("name", String(16), nullable=False),
Column("fullname", String(30)),
Column("nickname", String(50), nullable=True),
)
# 等价于 SELECT name,fullname FROM User WHERE id=1
# select() 方法构造 select 对象,where() 方法构造 where 对象
# 组合 select 对象和 where 对象来构造 SQL 语句
stem = select(User.name, User.fullname).where(User.id == 1)
# 执行构造好的对象来与数据库通信
with engine.connection() as conn:
conn.execute(stem)
# ORM
from sqlalchemy import Integer, String, ForeignKey,create_engine
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
# SQLAlchemy 应用的起点
engine = create_engine(URI)
# 相当于构建 MetaData 即数据库
class Base(DeclarativeBase):
pass
# 构建表映射
# 其中的每个属性对应表中的行
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
fullname: Mapped[str] = mapped_column(String(30))
nickname: Mapped[Optional[str]]
# 直接操作 Session 对象,状态会自动维护
with Session(engine) as session:
# 构建对象,对应数据库表中的行, User 是编写的对象模型
squidward = User(name="squidward", fullname="Squidward Tentacles")
# IdentitySet([User(id=None, name='squidward', fullname='Squidward Tentacles')]) 此时 id = None
krabs = User(name="ehkrabs", fullname="Eugene H. Krabs")
# 添加到 session 中
session.add(squidward)
session.add(krabs)
# 刷新状态到数据库中,此时 SQLAlchemy ORM 会自动构建 Insert 语句
session.flush()
# 此时数据状态变为 persistent 即持久态
print(squidward.id) # 4 可以看到插入数据的同时更新的 session 的数据状态
# 获取 User 中 id=1 的数据
# 同样会有 SQLAlchemy ORM 自动构造 Select 语句来执行
# 获取的数据直接就是持久态的
session.get(User, 1)
# 其他的增删改查都是一个道理
Note
SQLAlchemy Core
的 API 通常位于 sqlalchemy
命名空间中,而 SQLAlchemy ORM
位于 sqlalchemy.orm
命名空间中。DBAPI 则位于 sqlalchemy.dialects
命名空间中。