SQLAlchemy 介绍
经常编程的朋友应该知道,一般在开发软件,都会使用到数据库,就是我们常说的RDBMS。如果涉及到数据库,那么必不可少的就会使用SQL来操作数据库,但是大多数程序员朋友对于SQL这种DSL,并不太喜欢,那么除了SQL,我们可以通过编程语言中开发出的对象关系映射(ORM)来完成对于数据库的操作。比较有名的框架,包括Java里面的JPA, django框架里面自带的ORM等等,今天我们要介绍的主角,是Python世界里面的另外一个ORM框架-SQLAlchemy。
SQL Alchemy 是当前Python世界里最受欢迎的一款ORM框架,那么最主要的功能,一定是将我们的程序代码里的模型,映射到数据里的表和字段,相当把程序里的对象,映射到了数据库里的关系,就是对象关系映射。
架构图
图一 整体架构
其中虚线部分就是SQLAlchemy的整个结构,包括了ORM和Core两个模块,Core模块是和ORM独立开的,主要功能是通过对象构建SQL表达式,并且可以在目标数据库在一个事务里执行,并获得结果集。而ORM就是将我们的Python语言中的对象转换成相关表的模块。那么我们就来看看使用SQLAlchemy来操作数据库吧,但是操作之前,我们先了解几个基本概念。
Engine(引擎)
在进行和任意数据库进行交互前,SQLAlchemy需要建立一个Engine作为连接的中心来源,并且在其中配制好连接池的信息。通过架构图可得知,这个Engine需要和底层具体的连接池和方言关联,并且使用DBAPI作为底层实现。DBAPI实际上是一个数据库连接的接口,不同的数据库可以有不同的实现,来完成数据库的相关操作。一般来说创建引擎,就是指定一个连接协议,并且配置好相关的连接池。你可以认为Engine就是数据库的一个配置,示例如下:
python
from sqlalchemy import create_engine
engine = create_engine("sqlite+pysqlite:///:memory:", echo=True)
Connection(连接)
为了使程序能够和数据库对话,我们必须得创建一个Connection(连接),很自然的,我们可以使用一个Engine来创建Connection,由于是Connection是一个开放的资源,所以我们在使用的时候,必须使用受限的语法,一般可以使用Python的上下文管理器来进行创建。如下是一个简单示例
python
from sqlalchemy import text
with engine.connect() as conn:
result = conn.execute(text("select 'hello world'"))
print(result.all())
使用如上的命令,实际上在内部,是使用DBAPI创建了一个事务,并且执行了相关的SQL语句,但是这个事务,是没有提交的,在执行完后,会默认自动的回滚。如果需要在执行完提交,需要显式的在最后写一个conn.commit(),来完成这个事务。
Result (结果)
当我们使用SQLAlchemy来进行查询操作后,我们就会得到一个Result对象,例如如下代码:
python
with engine.connect() as conn:
result = conn.execute(text("SELECT x, y FROM some_table"))
for row in result:
print(f"x: {row.x} y: {row.y}")
x: 1 y: 1
x: 2 y: 4
x: 6 y: 8
x: 9 y: 10
ROLLBACK
这个result 就是一个Result对象, 对于它一般支持以下几种操作。
-
把他赋值给一个Tuple
-
index来找到第几列
-
使用列名来找到对应的列
-
使用映射来访问
python
# assign to tuple
result = conn.execute(text("select x, y from some_table"))
for x, y in result:
...
# Integer index
result = conn.execute(text("select x, y from some_table"))
for row in result:
x = row[0]
# Integer index
result = conn.execute(text("select x, y from some_table"))
for row in result:
y = row.y
# illustrate use with Python f-strings
print(f"Row: {row.x} {y}")
# Mapping access
result = conn.execute(text("select x, y from some_table"))
for dict_row in result.mappings():
x = dict_row["x"]
y = dict_row["y"]
Metadata(元数据)
Metadata是指保存一组Table对象的容器, 并且他可以保存和Engine或者Connection的关系。具体是如何使用的呢,首先我们会构造一个Metadata对象, 然后在创建我们的Table对象
python
from sqlalchemy import MetaData
metadata_obj = MetaData()
from sqlalchemy import Table, Column, Integer, String
user_table = Table(
"user_account",
metadata_obj,
Column("id", Integer, primary_key=True),
Column("name", String(30)),
Column("fullname", String),
)
可以看到,我们在创建表的时候,是要指定将相关的元数据加到了指定的metadata对象上面。以上是使用命令方式来创建表的,除此之外,我们还可以使用ORM来进行表的创建。而Table对象,就是和数据库的表对应,可以看到对应的字段名:
python
user_table.c.name
user_table.c.keys()
还可以查看和指定约束:
python
# find the primary key
user_table.primary_key
# add Foreign Key to table
from sqlalchemy import ForeignKey
address_table = Table(
"address",
metadata_obj,
Column("id", Integer, primary_key=True),
Column("user_id", ForeignKey("user_account.id"), nullable=False),
Column("email_address", String, nullable=False),
)
当我们把表都信息都放入metadata对象中,加上数据库的连接信息,相当于整个数据库的结构,通过metadata都已经保存下来了,所以我们可以通过metadata对象来生成我们的DDL了。
python
metadata_obj.create_all(engine)
Metadata不仅能够使用命令方式创建,还可以使用声明的方式创建。首先我们需要使用一个Base类,它需要继承与DeclarativeBase, 可以看到在Base对象里面带有一个metadata对象
python
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
Base.metadata
有了这个Base对象,我们就可以通过声明式的方式来创建表了。
python
from typing import List
from typing import Optional
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class User(Base):
__tablename__ = "user_account"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(30))
fullname: Mapped[Optional[str]]
addresses: Mapped[List["Address"]] = relationship(back_populates="user")
def __repr__(self) -> str:
return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
class Address(Base):
__tablename__ = "address"
id: Mapped[int] = mapped_column(primary_key=True)
email_address: Mapped[str]
user_id = mapped_column(ForeignKey("user_account.id"))
user: Mapped[User] = relationship(back_populates="addresses")
def __repr__(self) -> str:
return f"Address(id={self.id!r}, email_address={self.email_address!r})"
当创建完这几个类后,我们会将相关信息,加入到DelarativeBase的__table__ 对象中, 我们使用Mapped类型,来表示一个列,而在后面使用mapped_column()方法来映射数据库的列。而我们创建的Base类里面,也是包含metadata的,所以创建表的DDL,也可以通过以下的方法来生成。
python
Base.metadata.create_all(engine)
动态创建
SQLAlchemy还有一个厉害的地方,是我们可以不需要定义metadata,也就是不知道表的定义,而是通过读取当前数据库的状态,来自动生成表的模型,这个特性叫做table reflection(表反射),一般是使用如下的方式:
some_table = Table("some_table", metadata_obj, autoload_with=engine)
其中some_table是数据库中的表名,SQLAlchemy会去数据库中搜索相关的表名, metadata_obj是你的表将要放入的metadata对象,engine就是你的数据库Engine对象。
上面就是如何使用SQLAlchemy来和数据库建立连接,操作数据库,以及ORM的基本概念,如果你对SQLAlchemy感兴趣, 后续的文章中我会展开更多细节,带你继续了解这个强大的工具。
参考文档: SQLAlchemy 官方文档。