【译】SQLAlchemy文档:SQLAlchemy 统一教程

news2024/11/24 19:53:57

SQLAlchemy Unified Tutorial

SQLAlchemy 是 Python SQL工具包ORM,它为应用程序开发人员提供了 SQL 的全部功能和灵活性。它提供了一整套企业级持久性模式,专为高效和高性能的数据库访问而设计。
在这里插入图片描述

SQLAlchemy呈现为两层API:CoreORMORM构建在Core的基础上。

  • Core是基础,提供了 数据库连接、查询、构造SQL语句等工具。
  • ORM基于Core构建,提供对象关系映射(object relational mapping)

建立连接–Engine

SQLAlchemy程序的开始都要创建Engine对象,用于连接数据库。

下面我们使用内存中的SQLite数据库,创建一个Engine对象。

from sqlalchemy import create_engine
engine = create_engine("sqlite+pysqlite:///:memory:", echo=True)

create_engine的参数是字符串,包含了数据库的连接信息。echo=True表示打印日志。

"sqlite+pysqlite:///:memory:"包含3个信息:

  • sqlite: SQLite数据库
  • pysqlite: DBAPI
  • memory:内存数据库

使用事务和DBAPI

获取连接

Engine对象的唯一目的是提供与数据库的连接(Connection)。

from sqlalchemy import text

with engine.connect() as conn:
    result = conn.execute(text("select 'hello world'"))
    print(result.all())

在上面的示例中,上下文管理器提供了数据库连接,并在事务中构建了操作。
Python DBAPI 的默认行为包括事务始终在进行中;当释放连接范围时,将发出 ROLLBACK 以结束事务。事务不会自动提交;当我们想要提交数据时,我们通常需要调用 Connection.commit()

commit

  1. commit as you go.
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()
  1. begin once:
    另一种提交数据的方式,预先声明“连接”块是一个事务块。使用 Engine.begin() 方法来获取连接。
    此方法既可以管理事务的范围 Connection ,又可以将所有内容包含在事务中,并在最后使用 COMMIT(假设区块成功)或 ROLLBACK,以防出现异常。这种样式称为 begin once:
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}],
    )

begin once的风格通常是首选,因为它更简洁,并且预先表明了整个块的意图。但是,在本教程中,我们通常会使用“commit as you go”样式,因为它在演示方面更加灵活。

执行语句 基础

获取行

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}")

发送参数

with engine.connect() as conn:
    result = conn.execute(text("SELECT x, y FROM some_table WHERE y > :y"), {"y": 2})
    for row in result:
        print(f"x: {row.x}  y: {row.y}")

发送多个参数
(列表, 多次调用(executemany))

with engine.connect() as conn:
    conn.execute(
        text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
        [{"x": 11, "y": 12}, {"x": 13, "y": 14}],
    )
    conn.commit()

使用ORM Session

使用 ORM 时,基本的事务/数据库交互对象称为Session .在现代 SQLAlchemy 中,此对象的使用方式与 Connection 非常相似,实际上,当使用Session时 ,它内部使用Connection对象。

from sqlalchemy.orm import Session

stmt = text("SELECT x, y FROM some_table WHERE y > :y ORDER BY x, y")
with Session(engine) as session:
    result = session.execute(stmt, {"y": 6})
    for row in result:
        print(f"x: {row.x}  y: {row.y}")

类似Connection, Session也有“commit as you go”的方式,使用Session.commit()提交数据。

with Session(engine) as session:
    result = session.execute(
        text("UPDATE some_table SET y=:y WHERE x=:x"),
        [{"x": 9, "y": 11}, {"x": 13, "y": 15}],
    )
    session.commit()

使用数据库元数据

随着Engine 和 SQL 语句的结束,我们准备开始一些炼金术。SQLAlchemy Core 和 ORM 的核心元素是 SQL 表达式语言,它允许流畅、可组合的 SQL 查询构造。这些查询的基础是表示表和列等数据库概念的 Python 对象。这些对象统称为数据库元数据

SQLAlchemy 中数据库元数据最常见的基础对象称为 MetaDataTableColumn

Table

当我们使用关系数据库时,我们从中查询的数据库中的基本数据保存结构称为。在 SQLAlchemy 中,数据库“表”最终由一个类似名称 Table 的 Python 对象表示。

我们总是从一个集合开始,该集合将作为我们放置表的位置, 即MetaData对象。此对象本质上是围绕 Python 字典的外观,该字典存储一系列键入其字符串名称的 Table 对象。

from sqlalchemy import MetaData
metadata_obj = MetaData()

然后我们创建Table对象。
该模型具有一个名为的 user_account 表,用于存储网站的用户,以及一个关联表 address ,用于存储 user_account 与表中的行关联的电子邮件地址。
当完全不使用 ORM 声明性模型时,我们直接构造每个 Table 对象,通常将每个对象分配给一个变量:

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),
)

当我们想引用数据库中 user_account 表时,我们将使用 user_table变量来引用它。

Table的组成:
我们发现,Table对象类似SQL CREATE TABLE语句的结构。它包含表的名称,列的名称和类型。

  • Table:表
  • Column:列,可通过table.c.列名 访问
user_table.c.name

user_table.c.keys()

简单约束

user_table表的第一个 Column 包含参数Column.primary_key,用于指示这应该 Column 是此表的主键。。主键通常是隐式声明的,由 PrimaryKeyConstraint构造,我们可以在 Table 对象上的 Table.primary_key 属性上看到它

最典型的显式声明的约束是对应于数据库外键约束的 ForeignKeyConstraint 对象。当我们声明彼此相关的表时,SQLAlchemy 使用这些外键约束声明的存在,不仅使它们在 CREATE 语句中发出到数据库,而且还有助于构造 SQL 表达式。

仅涉及单个列的外键约束 通常通过 ForeignKey进行声明。
下面我们声明第二个表address,该表 将具有引用该 user 表的外键约束:

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),
)

向数据库发出 DDL

我们已经有了包含2个表的元数据对象,现在我们可以使用MetaData.create_all()方法将这些表发送到数据库。

metadata_obj.create_all(engine)

MetaData 对象还具有一个 MetaData.drop_all() 方法,该方法将以与CREATE相反的顺序发出 DROP 语句。

使用 ORM 声明性表单定义表元数据

使用 ORM 时, MetaData 集合仍然存在,但它本身与通常称为声明性基的仅 ORM 结构相关联。获取新的声明性库的最便捷方法是创建一个新类,该类对 SQLAlchemy DeclarativeBase 类进行子类化

from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
    pass

上面的 Base 类就是我们所说的声明性基础。当我们创建作为 的 Base 子类的新类时,结合适当的类级指令,它们将在类创建时被建立为一个新的 ORM 映射类,每个类通常(但不限于)引用一个特定的 Table 对象。

声明映射类

建立 Base 类后,我们现在可以根据新类 User 和 Address 来定义 user_account 和 表 address 的 ORM 映射类。我们在下面说明最现代的声明形式,它由PEP 484类型注释驱动,使用特殊类型,该类型 Mapped 指示要映射为特定类型的属性:

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})"

从 ORM 映射向数据库发出 DDL

Base.metadata.create_all(engine)

Table Reflection

表反射是指通过读取数据库的当前状态来生成 Table 对象 和相关对象的过程。 (即上面操作的逆过程)

some_table = Table("some_table", metadata_obj, autoload_with=engine)

some_table

处理数据(CRUD) (Core的角度)

  • INSERT
  • SELECT
  • UPDATE and DELETE

通过ORM处理数据

插入行

类的实例表示行:

squidward = User(name="squidward", fullname="Squidward Tentacles")
krabs = User(name="ehkrabs", fullname="Eugene H. Krabs")

向Session添加对象:

session = Session(engine)

session.add(squidward)
session.add(krabs)

此时,对象仍然在“挂起”状态,即它们尚未被发送到数据库。

Flushing
flush会向数据库发送SQL语句推送更改:
session.flush()

通常不需要手动调用session.flush(),因为 Session 更改具有自动刷新的行为。

自动生成的主键

squidward.id
krabs.id

从主键获取对象

some_squidward = session.get(User, 4)
some_squidward

提交

session.commit()

Update

sandy = session.execute(select(User).filter_by(name="sandy")).scalar_one()

sandy.fullname = "Sandy Squirrel"

当Session 发送flush时,将发出一个 UPDATE,用于更新数据库中的此值。
如前所述,在我们发出任何 SELECT 之前,会自动刷新。我们可以直接从此行查询列, 并返回更新后的值:

sandy_fullname = session.execute(select(User.fullname).where(User.id == 2)).scalar_one()
print(sandy_fullname)

Delete

patrick = session.get(User, 3)
session.delete(patrick)

Bulk / Multi Row INSERT, upsert, UPDATE and DELETE

https://docs.sqlalchemy.org/en/20/orm/queryguide/dml.html#orm-expression-update-delete

Rolling Back (回滚)

session.rollback() 会撤消所有未提交的更改。

关闭session

session.close()

ORM Related Objects

我们先回顾一下之前的relationship()

from sqlalchemy.orm import Mapped
from sqlalchemy.orm import relationship


class User(Base):
    __tablename__ = "user_account"

    # ... mapped_column() mappings

    addresses: Mapped[List["Address"]] = relationship(back_populates="user")


class Address(Base):
    __tablename__ = "address"

    # ... mapped_column() mappings

    user: Mapped["User"] = relationship(back_populates="addresses")

上面, User类有User.addresses属性 ,而 Address类有Address.user属性。 relationship()Mapped一起用于检查映射到 Address和User 对象之间的表关系。由于表示表的 Table 对象具有 ForeignKeyConstraint 指称 user_account 表的对象, relationship()可以明确地确定从 User 类到 Address 类之间存在着一对多的关系; user_account 表中的一行可以包含address表中的多行(一个用户可以有多个邮箱地址)。

一对多关系自然对应于另一个方向上的多对一关系,在本例中为 Address.user 。上面在引用另一个名称的两个 relationship() 对象上配置的 relationship.back_populates 参数确定这两个 relationship() 构造中的每一个都应被视为相互互补;我们将在下一节中看到这是如何发挥作用的。

持久化和加载关系

果我们创建一个新 User 对象,我们可以注意到,当我们访问该 .addresses 元素时,有一个 Python 列表:

u1 = User(name="pkrabs", fullname="Pearl Krabs")
u1.addresses  # []

使用list.append() 方法,我们可以添加一个 Address 对象:

a1 = Address(email_address="pearl.krabs@gmail.com")
u1.addresses.append(a1)

再看u1.addresses,发现已经添加成功了:

u1.addresses 
# [Address(id=None, email_address='pearl.krabs@gmail.com')]

此外,u1也被a1关联了:

a1.user 
# User(id=None, name='pkrabs', fullname='Pearl Krabs')

这种同步是由于我们在两个 relationship() 对象之间使用了参数 relationship.back_populates 。此参数命名另一个应发生互补属性赋值/列表突变的参数 relationship() 。它在另一个方向上同样有效,即如果我们创建另一个 Address 对象并赋值给其 Address.user 属性,它 Address 将成为该 User 对象 User.addresses 集合的一部分:

a2 = Address(email_address="pearl@aol.com", user=u1)
u1.addresses
# [Address(id=None, email_address='pearl.krabs@gmail.com'), Address(id=None, email_address='pearl@aol.com')]

Cascading Objects

当我们用Session.add() 添加User 对象时,相关 Address 对象也会被添加到该 Session 对象中:

session.add(u1)
u1 in session
a1 in session
a2 in session
# 3 True

上述行为,即 Session 接收到一个 User 对象,并沿着 User.addresses 关系找到相关 Address 对象,称为保存-更新级联(save-update cascade)。

这三个对象现在处于挂起状态;这意味着他们已准备好成为 INSERT 操作的对象,但尚未进行;这三个对象都尚未分配主键,此外, a1 和 a2 对象具有user_id属性,该属性引用的Column具有ForeignKeyConstraint (关于user_account.id) 的属性;这些也是 None, 因为对象尚未与实际数据库行关联:

print(u1.id)
print(a1.user_id) 
# None None

当使用 Session 时,所有这些乏味的事情都会为我们处理:我们使用 Session.commit() 提交时,所有步骤都以正确的顺序调用,此外,新生成的 user_account 行主键被适当地应用于 address.user_id 列:

session.commit()

Loading Relationships

延迟加载…

在查询中使用关系

JOIN

print(select(Address.email_address).select_from(User).join(User.addresses))
print(select(Address.email_address).join_from(User, Address))

WHERE
https://docs.sqlalchemy.org/en/20/orm/queryguide/index.html

加载策略?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1817391.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

龙迅LT9611UXC 2 PORT MIPIDSI/CSI转HDMI 2.1,支持音频IIS/SPDIF输入,支持标准4K60HZ输出

龙迅LT9611UXC描述: LT9611UXC是一个高性能的MIPI DSI/CSI到HDMI2.0转换器。MIPI DSI/CSI输入具有可配置的单端口或双端口,1高速时钟通道和1~4高速数据通道,最大2Gbps/通道,可支持高达16Gbps的总带宽。LT9611UXC支持突发模式DSI视…

GStreamer安装——iOS

安装iOS开发 支持从iOS6开始的所有版本 先决条件 iOS开发需要下载Xcode和iOSSDK。Xcode 可以在App Store或 这里 iOSSDK,如果它还没有包含在您的Xcode版本中, 可以从下载选项卡下的Xcode首选项菜单下载。 最低要求iOS版本为6.0。的最低要求版本 Xcode…

Android帧绘制流程深度解析 (二)

书接上回:Android帧绘制流程深度解析 (一) 5、 dispatchVsync: 在请求Vsync以后,choreographer会等待Vsync的到来,在Vsync信号到来后,会触发dispatchVsync函数,从而调用onVsync方法…

栈(Stack)汇总

栈简介 栈(Stack)是只允许在一端进行插入或者删除操作的线性表。它的操作特性可以概括为——后进先出(Last In First Out,LIFO)。栈顶(Top)——线性表允许进行插入删除的一端; 栈底…

SwaggerSpy:一款针对SwaggerHub的自动化OSINT安全工具

关于SwaggerSpy SwaggerSpy是一款针对SwaggerHub的自动化公开资源情报(OSINT)安全工具,该工具专为网络安全研究人员设计,旨在简化广大红队研究人员从SwaggerHub上收集已归档API信息的过程,而这些OSINT信息可以为安全人…

BigDecimal-解决java中的浮点运算

《阿里巴巴 Java 开发手册》中提到:“为了避免精度丢失,可以使用 BigDecimal 来进行浮点数的运算”。浮点数的运算竟然还会有精度丢失的风险吗?确实会! 示例代码: float a 2.0f - 1.9f; float b 1.8f - 1.7f; Syst…

制作带有目录的电子书

有时候想自己制作一些.mobi格式的电子书在kindle上进行阅读,有两种简单做法。 方法一: 工具:markdown编辑器、calibre 在markdown编辑器中编辑想要制作电子书的文本,在想要设置目录的地方加一个#(我只制作了一级标题…

线程安全问题【snychornized 、死锁、线程通信】

目录 一、线程安全1.1 线程安全问题?1.2 如何解决线程安全问题方法具体如何实现? 1.3 同步方法1.4 同步代码块1.5 总结1.6 售票例子1.8 补充 二、线程安全的集合三、死锁【了解】四、线程通信4.1 同步方法4.2 同步代码块4.3 wait和sleep本篇的思维导图 最后 一、线程安全 1.…

使用PHP对接企业微信审批接口的问题与解决办法(二)

在现代企业中,审批流程是非常重要的一环,它涉及到企业内部各种业务流程的规范和高效运转。而随着企业微信的流行,许多企业希望将审批流程整合到企业微信中,以实现更便捷的审批操作。本文将介绍如何使用PHP对接企业微信审批接口&am…

大数据学习——安装hive

一. 安装准备 1. 打开虚拟机,启动配置了NameNode节点的虚拟机(一般和mysql在同一台虚拟机)并连接shell 二. 安装 1. 上传hive安装包 hive安装包 提取码:6666 切换到/opt/install_packages目录下 可以将之前解压的rpm文件删除…

高效数据架构:分表流程实践

前言 ​ 随着业务的不断扩展,数据量激增成为不可避免的现象。当数据量达到某一临界点时,单一的数据表可能无法承载如此庞大的数据量,此时就需要考虑进行分库分表的策略。尽管业界普遍认为数据量达到1000万时就应考虑分表,但实际上…

线程池 (重点)概述7大参数理解

目录 1、线程池思想概述 2、什么是线程池? 3、不使用线程池的问题 4、线程池的工作原理 5、线程池实现的API、参数说明 5.1、谁代表线程池? 5.2、如何得到线程池对象 5.3、ThreadPoolExecutor构造器的参数说明 6、线程池常见面试题 6.1、临时线程什么时候…

Vue + Asp.NET调试时出现的证书问题 (OpenSSL)

Vue Asp.NET调试时出现的证书问题 1. 证书过期问题步骤一:创建新的私钥步骤 2: 创建新的证书签名请求(CSR)步骤 3: 使用 CSR 和 CA 私钥签署新证书步骤 4: 替换或使用新证书 2. 证书不受信任问题步骤: 3. 安全证书不指定使用者可选名称步骤一: 删除已生…

java实现文件的压缩及解压

一、起因 开发中需要实现文件的压缩及解压功能,以满足某些特定场景的下的需要,在此说下具体实现。 二、实现 1.定义一个工具类ZipUtils,实现文件的压缩及解压,代码如下: import java.io.*; import java.nio.charset.Charset; impo…

网络安全等级保护基本要求 第1部分:安全通用要求

基本要求 第三级 安全物理环境 物理位置选择 a) 机房场地应选择在具有防震、防风和防雨等能力的建筑内; b) 机房场地应避免设在建筑物的顶层或地下室,否则应加强防水和防潮措施 物理访问控制 a) 机房出入口应配置电子门禁系统,控制、鉴…

Linux 基本指令3

date指令 date[选项][格式] %Y--年 %m--月 %d--日 %H--小时 %M--分 %S--秒 中间可用其他符号分割,不能使用空格。 -s 设置时间,会返回设置时间的信息并不是改变当前时间 设置全部时间年可用-或者:分割日期和时间用空格分隔&#xff…

自动驾驶场景下TCP协议参数优化调整案例分享

RTT 往返时间,从tcp协议栈决定发包,到收到回包的时间。 包含本地驱动,网卡硬件,网线,交换机,收包方处理的耗时。需注意如果开了delayed ack,协议栈未做特殊处理(默认没做&#xff…

攻防演练之-成功的钓鱼邮件溯源

书接上文,《网络安全攻防演练风云》专栏之攻防演练之-网络安全产品大巡礼二,这里。 演练第一天并没有太大的波澜,白天的时间过得很快。夜色降临,攻防演练中心内的灯光依旧明亮。对于网络安全团队来说,夜晚和白天并没有…

基于STM32和人工智能的智能家居监控系统

目录 引言环境准备智能家居监控系统基础代码实现:实现智能家居监控系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统4.4 用户界面与数据可视化应用场景:智能家居环境监控与管理问题解决方案与优化收尾与总结 1. 引言 随着智能家居技术的发展&…

如何从微软官方下载Edge浏览器的完整离线安装包

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 正文内容 📒🚀 官方直链下载🚬 手动选择下载🎈 获取方式 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 在网上搜索Microsoft Edge浏览器的离线安装包时,很多用户都会发现大部分都是在线安装包,无法满足他们在无网络环境下进…