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

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 textwith 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 Sessionstmt = 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.nameuser_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 relationshipclass 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 relationshipclass User(Base):__tablename__ = "user_account"# ... mapped_column() mappingsaddresses: Mapped[List["Address"]] = relationship(back_populates="user")class Address(Base):__tablename__ = "address"# ... mapped_column() mappingsuser: 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.mzph.cn/pingmian/27697.shtml

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

相关文章

程序优化 --- arthas trace命令使用

最近在做优化,通过arthas的trace命令去观察方法内的耗时情况以便对程序进行修改. 1.启动arthas之后选择需要监测的程序 2.找到需要监测的接口,一般都是直接找service例子如下: trace 类地址.类名 方法名 (中间有空格)

代码随想录算法训练营第39天(py)| 动态规划 | 62.不同路径、 63. 不同路径 II

62.不同路径 力扣链接 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路…

Nginx实战:故障处理_后端服务正常,nginx偶发502(Bad Gateway)

一、故障场景 用户访问服务偶发报错【502 Bad Gateway】,但是服务后端正常运行。架构如下: #mermaid-svg-4dDszusKEuPgIPlt {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-4dDszusKEuPgIPlt…

Qt项目天气预报(1) - ui界面搭建

ui中部 效果演示 ui效果 显示效果 控件列表 配合右图查看 居中对齐-label 设置label居中对齐(别傻傻的空格对齐了) 间距配置 widget03 外围的widget对象: 包含label 和 widget0301,如下图 widget0301 内围的widget对象,如下图 样式表 widget03 …

自养号测评全揭秘:沃尔玛卖家如何实现销量稳步增长

在沃尔玛这片激烈的商业战场上,我作为一位销售者,深知在这个竞争激烈的市场中,测评对于提升产品排名、权重以及销量的关键作用。随着测评需求的不断增长,我们意识到,寻找可靠的测评服务不再是简单的选择,而…

【scikit-learn入门指南】:机器学习从零开始

1. 简介 scikit-learn是一款用于数据挖掘和数据分析的简单高效的工具,基于NumPy、SciPy和Matplotlib构建。它能够进行各种机器学习任务,如分类、回归和聚类。 2. 安装scikit-learn 在开始使用scikit-learn之前,需要确保已经安装了scikit-le…

pyinstall打包exe报错

1- 报错 Please install pywin32-ctypes. 前提:python安装路径中已经安装了pywin32-ctypes。 运行pyinstaller报错 PyInstaller cannot check for assembly dependencies. Please install pywin32-ctypes. 解决思路: python安装路径下Lib\site-packa…

算法:位运算题目练习

目录 常见的位运算的操作总结 ①基础位操作 ②给一个数n,确定它的二进制表示中的第x位是0还是1 ③将一个数n的二进制表示的第x位修改成1 ④将一个数n的二进制表示的第x位修改成0 ⑤位图的思想 ⑥提取一个数n二进制表示中最右侧的1 ⑦干掉一个数n二进制表示中…

JVM 根可达算法

Java中的垃圾 Java中"垃圾"通常指的是不再被程序使用和引用的对象,具体表现在没有被栈、JNI指针和永久代对象所引用的对象。Java作为一种面向对象的编程语言,它使用自动内存管理机制,其中垃圾收集器负责检测和回收不再被程序引用的…

响应式德米拉数字内容交易系统素材下载站模板

★模板说明★ 该数字交易系统设计非常完美,两种响应式模式,可打开边栏模式和盒子模式;八种网站颜色,四种风格颜色可供用户自行选择,还可在网站选背景图片;完美的分成系统、充值功能、个人中心等等都以html…

6.11 作业

以下是一个简单的比喻,将多态概念与生活中的实际情况相联系: 比喻:动物园的讲解员和动物表演 想象一下你去了一家动物园,看到了许多不同种类的动物,如狮子、大象、猴子等。现在,动物园里有一位讲解员&…

找我设计官网的不多了,看到漂亮大气的,还是忍不住分享出来。

现在有客户找我做官网设计,我说:要么搞个高大上个性化定制的,要么就选个模板得了,几千元的网站不上不下,不如不做。 分享一批高大上的网站给老铁们看看。

功能强大的API函数FindFirstFile使用介绍(附源码)

在处理文件的相关代码中,会频繁使用到Windows系统API函数FindFirstFile,这个函数功能很强大,很多功能都不开它。本文就根据我们在项目中使用该函数的情况,来大概地梳理一下使用FindFirstFile都可以实现哪些常用的功能。 1、FindFirstFile函数声明与WIN32_FIND_DATA结构体 我…

C++ 33 之 const 修饰静态成员

#include <iostream> #include <string.h> using namespace std;// 定义静态const数据成员时&#xff0c;最好在类内部初始化,避免在类外重复初始化&#xff0c;也为了代码的可读性和可维护性class Students03{ public:// 两种写法都可以const static int s_a 10;…

如何完美解决升级 IntelliJ IDEA 最新版之后遇到 Git 记住密码功能失效的问题

&#x1f6e0;️ 如何完美解决升级 IntelliJ IDEA 最新版之后遇到 Git 记住密码功能失效的问题 摘要 在这篇文章中&#xff0c;我们将详细探讨如何解决在升级到 IntelliJ IDEA 最新版&#xff08;2024.1.3 Ultimate Edition&#xff09;后遇到的 Git 记住密码功能失效的问题。…

FPGA - Verilog题目: 非整数倍数据位宽转换24to128

题目描述&#xff1a; 实现数据位宽转换电路&#xff0c;实现24bit数据输入转换为128bit数据输出。其中&#xff0c;先到的数据应置于输出的高bit位。 电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性&#xff0c;valid_out用来指示数据输出data_out的有效性…

zabbix老版本3.0.14迁移

由于之前zabbix版本过老&#xff0c;一直未能升级&#xff0c;现在一台老的服务器需要迁移&#xff0c;服务器较老&#xff0c;就不更新&#xff0c;先迁移数据&#xff0c; 下载地址&#xff1a; Zabbix Official Repository http://repo.zabbix.com/zabbix/3.0/rhel/7/x86…

数据结构之线性表(4)

前面我们了解到线性表中的顺序表、链表等结构&#xff0c;今天我们探讨新的一种线性表——栈。 那么我们开始栈的探讨之旅吧。 1.栈的基本概念 1.1栈&#xff08;Stack&#xff09;&#xff1a; 是只允许在一端进行插入或删除的线性表。首先栈是一种线性表&#xff0c;但限定…

对象和引用类型的赋值都是通过引用传递的方式进行的,这意味着变量实际上存储的是对象的引用,而不是对象本身的副本

这篇博客我主要想解释一下这句话&#xff1a;对象和引用类型的赋值都是通过引用传递的方式进行的&#xff0c;这意味着变量实际上存储的是对象的引用&#xff0c;而不是对象本身的副本。 其实这段话早在学习JS的时候就接触过&#xff0c;只是被我丢进了“记忆垃圾桶”&#xf…

[大模型]Llama-3-8B-Instruct FastApi 部署调用

环境准备 在 Autodl 平台中租赁一个 3090 等 24G 显存的显卡机器&#xff0c;如下图所示镜像选择 PyTorch-->2.1.0-->3.10(ubuntu22.04)-->12.1。 接下来打开刚刚租用服务器的 JupyterLab&#xff0c;并且打开其中的终端开始环境配置、模型下载和运行演示。 pip 换源…