一、分类
1.SQLAlchemy 以两个不同的 API 呈现,一个构建在另一个之上。这些 API 被称为 Core 和 ORM
2.SQLAlchemy Core 是 SQLAlchemy 作为“数据库工具包”的基础架构。该库提供了用于管理数据库连接、与数据库查询和结果交互以及程序化构建 SQL 语句的工具。主要为 Core-only 的章节不会提及 ORM。在这些章节中使用的 SQLAlchemy 构造将从 sqlalchemy 命名空间导入。作为主题分类的额外指示,它们还将在右侧包含深蓝色边框。当使用 ORM 时,这些概念仍然在起作用,但在用户代码中不常显式出现。ORM 用户应该阅读这些章节,但不希望直接为以 ORM 为中心的代码使用这些 API。
3.SQLAlchemy ORM 构建在 Core 之上,以提供可选的 对象关系映射 功能。ORM 提供了一个额外的配置层,允许用户定义的 Python 类 映射 到数据库表和其他构造,以及一种称为 会话 的对象持久化机制。然后,它扩展了 Core 级别的 SQL 表达式语言,以允许根据用户定义的对象组成和调用 SQL 查询。主要为 ORM-only 的章节应标题包含短语“ORM”,以便清楚表明这是一个与 ORM 相关的主题。在这些章节中使用的 SQLAlchemy 构造将从 sqlalchemy.orm 命名空间导入。最后,作为主题分类的额外指示,它们还将在左侧包含浅蓝色边框。
下面是用Core进行讲解的
二、建立连接
1.解释
每个连接到数据库的 SQLAlchemy 应用程序都需要使用 Engine(此对象充当特定数据库连接的中心来源,既提供工厂,又为这些数据库连接提供名为 连接池 的持有空间)。
2.语法:
from sqlalchemy import create_engine
engine = create_engine("sqlite+pysqlite:///:memory:", echo=True)
参数说明:
(1)第一个参数表示:与哪个数据库连接进行连接、我们正在使用什么 DBAPI(此时使用的是pysqlite,若省略则默认使用DBAPI)、如何定位数据库(/:内存数据库:,它是 sqlite3 模块的指示符,表明我们将使用仅在内存中的数据库,若是在mysql中的数据库可表示如下/user:password@localhost(或者是IP地址):端口号/要用的数据库名)。
(2)echo = True: 此标志是设置 python的更正式日志记录的简写方式。
3.使用事务和 DBAPI
Engine 的目的是通过提供 Connection 对象来连接数据库。当直接使用 Core 时,Connection 对象是与数据库进行所有交互的方式。由于 Connection 会创建针对数据库的开放资源,我们希望将此对象的使用限制在特定上下文中。最好的方法是使用 Python 上下文管理器,也称为 with 语句。下面我们使用文本 SQL 语句来显示 “Hello World”。文本 SQL 是使用名为 text() 的构造创建的并返回result对象
#第一种有engine的连接
from sqlalchemy import text
with engine.connect() as conn:
result = conn.execute(text("select 'hello world'"))
print(result.all())
#第二种无engine的连接
from sqlalchemy import Connection
con = Connection(
host='192.168.31.130',
port=3306,
user='root',
password='123456',
database='flask_1',
autocommit=True,#自动提交事务
)
cur = con.cursor()
sql = 'select * from comment where text!=""'
cur.execute(sql)
print(cur.fetchall())
cur.close()#断开连接
在上面的示例中,上下文管理器创建数据库连接并在事务中执行操作,Python DBAPI 的默认行为是事务始终在进行中;当连接释放时,会发出 ROLLBACK 以结束事务。事务不会自动提交;如果我们想提交数据,我们需要调用 Connection.commit(),此为逐个提交
with engine.connect() as conn:
conn.execute(text("CREATE TABLE some_table (x int, y int)"))
conn.execute(
text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
[{"x": 1, "y": 1}, {"x": 2, "y": 4}],
)
conn.commit()
#mysql中现实的格式
BEGIN (implicit)
CREATE TABLE some_table (x int, y int)
[...] ()
<sqlalchemy.engine.cursor.CursorResult object at 0x...>
INSERT INTO some_table (x, y) VALUES (?, ?)
[...] [(1, 1), (2, 4)]
<sqlalchemy.engine.cursor.CursorResult object at 0x...>
COMMIT
其中“implicit” 这里意味着 SQLAlchemy 实际上没有向数据库发送任何命令;它只是认为这是 DBAPI 隐式事务的开始。您可以注册 事件钩子 来拦截此事件
我们使用 Engine.begin() 方法获取连接,而不是Engine.connect() 方法。此方法将理 Cnnection 的范围,并将所有内容都包含在事务中,如果代码块成功,则在末尾使用COMMIT,如果引发异常,则使用 ROLLBACK。这种风格被称为一次开始
with engine.begin() as conn:
conn.execute(
text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
[{"x": 6, "y": 8}, {"x": 9, "y": 10}],
)
我们将首先更详细地说明 Result对象,方法是使用我们之前插入的行,对我们创建的表运行文本 SELECT 语句
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}")
Result有许多用于获取和转换行的方法,例如之前说明的Result.all()方法,它返回所有Row对象的列表。它还实现了 Python 迭代器接口,以便我们可以直接迭代Row对象的集合
-
元组赋值 - 这是最符合 Python 习惯的风格,即将变量按位置分配给接收到的每个行
-
整数索引 - 元组是 Python 序列,因此也提供常规整数访问
result = conn.execute(text("select x, y from some_table")) for row in result: x = row[0] -
属性名称 - 由于这些是 Python 命名元组,因此元组具有与每列名称匹配的动态属性名称。这些名称通常是 SQL 语句分配给每行中列的名称。虽然它们通常相当可预测,并且也可以通过标签来控制,但在不太明确的情况下,它们可能会受到数据库特定行为的影响
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}") -
映射访问 - 要将行接收为 Python 映射 对象,这本质上是 Python 用于常见
dict对象的只读接口,可以使用 Result.mappings() 修饰符将 Result 转换为 MappingResult 对象;这是一个结果对象,它生成类似字典的 RowMapping 对象,而不是 Row 对象result = conn.execute(text("select x, y from some_table")) for dict_row in result.mappings(): x = dict_row["x"] y = dict_row["y"]
三、使用Table对象
1.前提
用Table对象的前提,创建一个集合,该集合是放置表的MetaData对象
MetaData 是一个容器对象,用于将数据库(或多个数据库)被描述的许多不同功能组合在一起。
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),
)
用外键约束
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),#not null用nullable表示
)
向数据库发送DDL
MetaData.create_all()#让数据库出发create table语句
MetaData.drop_all()#让数据库触发drop语句
四、处理数据
1.insert():构造用于所有的Insert插入
from sqlalchemy import insert
#stmt为Insert实例
stmt = insert(user_table).values(name="spongebob", fullname="Spongebob Squarepants")
print(stmt)#返回INSERT INTO user_account (name, fullname) VALUES (:name, :fullname)
#字符串化形式是通过生成对象的Compiled形式来创建的
compiled = stmt.compile()
compiled = stmt.compile()#返回{'name': 'spongebob', 'fullname': 'Spongebob Squarepants'}
#用下面的语句,提交到数据库中。
with engine.connect() as conn:
result = conn.execute(stmt)
conn.commit()
如果我们采用一个尚未调用Insert.value()的Insert构造,并执行它而不是打印它,则该语句将根据我们传递给Connect.execute()方法的参数编译成字符串,并且仅包含与传递的参数相关的列。这实际上是使用Insert插入行的常用方法,而无需键入显式的 VALUES 子句
with engine.connect() as conn:
result = conn.execute(
insert(user_table),
[
{"name": "sandy", "fullname": "Sandy Cheeks"},
{"name": "patrick", "fullname": "Patrick Star"},
],
)
conn.commit()
深入了解Insert.values()方法
from sqlalchemy import select, bindparam
scalar_subq = (
select(user_table.c.id)
.where(user_table.c.name == bindparam("username"))
.scalar_subquery()
)
with engine.connect() as conn:
result = conn.execute(
insert(address_table).values(user_id=scalar_subq),
[
{
"username": "spongebob",
"email_address": "spongebob@sqlalchemy.org",
},
{"username": "sandy", "email_address": "sandy@sqlalchemy.org"},
{"username": "sandy", "email_address": "sandy@squirrelpower.org"},
],
)
conn.commit()
#上传到mysql后显示的格式
BEGIN (implicit)
INSERT INTO address (user_id, email_address) VALUES ((SELECT user_account.id
FROM user_account
WHERE user_account.name = ?), ?)
[...] [('spongebob', 'spongebob@sqlalchemy.org'), ('sandy', 'sandy@sqlalchemy.org'),
('sandy', 'sandy@squirrelpower.org')]
COMMIT
(1)RETURNING 子句会自动用于检索最后插入的主键值以及服务器默认值的值
insert_stmt = insert(address_table).returning(address_table.c.id, address_table.c.email_address)
print(insert_stmt)
#mysql中的格式
INSERT INTO address (id, user_id, email_address)
VALUES (:id, :user_id, :email_address)
RETURNING address.id, address.email_address
(2)与from_select()结合使用
select_stmt = select(user_table.c.id, user_table.c.name + "@aol.com")
insert_stmt = insert(address_table).from_select(
["user_id", "email_address"], select_stmt
)
print(insert_stmt.returning(address_table.c.id, address_table.c.email_address))
#上传到MySQL中的格式
INSERT INTO address (user_id, email_address)
SELECT user_account.id, user_account.name || :name_1 AS anon_1
FROM user_account RETURNING address.id, address.email_address
2.select():构造用于所有 SELECT 查询
from sqlalchemy import select
stmt = select(user_table).where(user_table.c.name == "spongebob")
print(stmt)
#mysql中显示
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name = :name_1
由于 SELECT 语句返回行,因此我们始终可以迭代结果对象以获取 Row 对象
with engine.connect() as conn:
for row in conn.execute(stmt):
print(row)
使用文本列表达式选择
from sqlalchemy import text
from sqlalchemy import literal_column
#方法一
stmt = select(text("'some phrase'"), user_table.c.name).order_by(user_table.c.name)
with engine.connect() as conn:
print(conn.execute(stmt).all())
#mysql中
BEGIN (implicit)
SELECT 'some phrase', user_account.name
FROM user_account ORDER BY user_account.name
[generated in ...] ()
'''[('some phrase', 'patrick'), ('some phrase', 'sandy'), ('some phrase', 'spongebob')]'''
ROLLBACK
#方法二
stmt = select(literal_column("'some phrase'").label("p"), user_table.c.name).order_by(
user_table.c.name
)
with engine.connect() as conn:
for row in conn.execute(stmt):
print(f"{row.p}, {row.name}")
#mysql
BEGIN (implicit)
SELECT 'some phrase' AS p, user_account.name
FROM user_account ORDER BY user_account.name
[generated in ...] ()
'''some phrase, patrick
some phrase, sandy
some phrase, spongebob'''
ROLLBACK
(1)WHERE子句
print(select(user_table).where(user_table.c.name == "squidward"))
#mysql
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name = :name_1
同时可以通过多个WHERE子句,并由“.”连接生成AND连接的多个表达式;或者是在where中用and_()和or_()函数,进行连接。
(2).join_from():用来连接两个表,显式指示 JOIN 的左侧和右侧
print(
select(user_table.c.name, address_table.c.email_address).join_from(
user_table, address_table
)
)
#mysql
SELECT user_account.name, address.email_address
FROM user_account JOIN address ON user_account.id = address.user_id
(3)join():用来连接两个表,仅只是 JOIN 的右侧,左侧靠推断
3.带有 GROUP BY / HAVING 的聚合函数
(1)使用名为func的命名空间来进行构造。
count()表示SQL COUNT函数
lower()将字符串转换为小写
now()用于提供当前日期和时间
group_by()表示GROUP BY函数
having()表示HAVING函数
order_by(' ',asc/desc(' '))表示ORDER BY 函数
alias()表示AS起别名
from sqlalchemy import func
from sqlalchemy import func, desc
count_fn = func.count(user_table.c.id)
print(count_fn)
with engine.connect() as conn:
result = conn.execute(
select(User.name, func.count(Address.id).label("count"))
.join(Address)
.group_by(User.name)
.having(func.count(Address.id) > 1)
)
print(result.all())
stmt = (
select(Address.user_id, func.count(Address.id).label("num_addresses"))
.group_by("user_id")
.order_by("user_id", desc("num_addresses"))
)
print(stmt)
user_alias_1 = user_table.alias()
user_alias_2 = user_table.alias()
print(
select(user_alias_1.c.name, user_alias_2.c.name).join_from(
user_alias_1, user_alias_2, user_alias_1.c.id > user_alias_2.c.id
)
)
4.子查询和CTE
(1)Subquery对象表示子查询,CTE表示CTE。
subquery()
cte()
scalar_subquery()表示标准子查询
subq = (
select(func.count(address_table.c.id).label("count"), address_table.c.user_id)
.group_by(address_table.c.user_id)
.subquery()
)
print(subq)
#进行子查询
print(select(subq.c.user_id, subq.c.count))
#共用表表达式
subq = (
select(func.count(address_table.c.id).label("count"), address_table.c.user_id)
.group_by(address_table.c.user_id)
.cte()
)
stmt = select(user_table.c.name, user_table.c.fullname, subq.c.count).join_from(
user_table, subq
)
print(stmt)
(2)相关子查询
我们使用correlate()或corrwlate_except()指定要关联的表(相关性不明确的情况下,SQLAlchemy 会让我们知道需要更清晰的说明,容易出错)
subq = (
select(func.count(address_table.c.id))
.where(user_table.c.id == address_table.c.user_id)
.scalar_subquery()
.correlate(user_table)
)
#返回此列数据
with engine.connect() as conn:
result = conn.execute(
select(
user_table.c.name,
address_table.c.email_address,
subq.label("address_count"),
)
.join_from(user_table, address_table)
.order_by(user_table.c.id, address_table.c.id)
)
print(result.all())
(3)EXISTS子查询
SQL EXISTS 关键字是一个运算符,与标量子查询一起使用,以返回布尔值 true 或 false,具体取决于 SELECT 语句是否会返回行.
subq = (
select(func.count(address_table.c.id))
.where(user_table.c.id == address_table.c.user_id)
.group_by(address_table.c.user_id)
.having(func.count(address_table.c.id) > 1)
).exists()
with engine.connect() as conn:
result = conn.execute(select(user_table.c.name).where(subq))
print(result.all())
5.update和delete
(1)update
from sqlalchemy import update
stmt = (
update(user_table)
.where(user_table.c.name == "patrick")
.values(fullname="Patrick the Star")
)
print(stmt)
#mysql
UPDATE user_account SET fullname=:fullname WHERE user_account.name = :name_1
Update.values()方法控制 UPDATE 语句的 SET 元素的内容
另一个仅 MySQL 的行为是,UPDATE 语句的 SET 子句中参数的顺序实际上会影响每个表达式的评估,ordered_values()接受元组序列,以便可以控制此顺序。
update_stmt = update(some_table).ordered_values(
(some_table.c.y, 20), (some_table.c.x, some_table.c.y + 10)
)
print(update_stmt)
(2)delete
from sqlalchemy import delete
stmt = delete(user_table).where(user_table.c.name == "patrick")
print(stmt)
多表删除
delete_stmt = (
delete(user_table)
.where(user_table.c.id == address_table.c.user_id)
.where(address_table.c.email_address == "patrick@aol.com")
)
from sqlalchemy.dialects import mysql
print(delete_stmt.compile(dialect=mysql.dialect()))
与returning语句一起使用
update_stmt = (
update(user_table)
.where(user_table.c.name == "patrick")
.values(fullname="Patrick the Star")
.returning(user_table.c.id, user_table.c.name)
)
print(update_stmt)
delete_stmt = (
delete(user_table)
.where(user_table.c.name == "patrick")
.returning(user_table.c.id, user_table.c.name)
)
print(delete_stmt)
2万+

被折叠的 条评论
为什么被折叠?



