【译】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.rhkb.cn/news/348721.html

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

相关文章

【记录】ChatGLM3-6B大模型部署、微调(一):部署

ChatGLM3介绍 源码连接: ChatGLM3 是智谱AI和清华大学 KEG 实验室联合发布的对话预训练模型。ChatGLM3-6B 是 ChatGLM3 系列中的开源模型,在保留了前两代模型对话流畅、部署门槛低等众多优秀特性的基础上,ChatGLM3-6B 引入了如下特性&#xf…

如何高效管理和监控 Elasticsearch 别名及索引?

0、引言 在 Elasticsearch 项目中,管理和监控索引是开发者的一项重要任务。 尤其是当我们需要在项目的管理部分展示索引和别名的统计信息时,了解如何有效地列出这些别名和索引显得尤为重要。 本篇博客将介绍几种在 Elasticsearch 中列出别名和索引的方法…

7.Nginx动静分离

介绍 把动态和静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。 动静分离从目前实现角度分为两种: 1.纯粹把静态文件独立成单独的域名,放在独立的静态资源服务器上,目前主流推崇的方案。 2.动态和静态文件混合在一起发布,通过nginx来分开。 通过loc…

【微信小程序】事件传参的两种方式

文章目录 1.什么是事件传参2.data-*方式传参3.mark自定义数据 1.什么是事件传参 事件传参:在触发事件时,将一些数据作为参数传递给事件处理函数的过程,就是事件传参 在微信小程序中,我们经常会在组件上添加一些自定义数据,然后在…

毕业年薪20w起!25届最近5南京邮电大学自动化考研院校分析

南京邮电大学 目录 一、学校学院专业简介 二、考试科目指定教材 三、近5年考研分数情况 四、近5年招生录取情况 五、最新一年分数段图表 六、历年真题PDF 七、初试大纲复试大纲 八、学费&奖学金&就业方向 一、学校学院专业简介 二、考试科目指定教材 1、考试…

点云格式转化:将 ros PointCloud2格式数据转为livox CustomMsg格式

前言 览沃科技有限公司(Livox)成立于2016年。为了革新激光雷达行业,Livox致力于提供高性能、低成本的激光雷达传感器。通过降低使用门槛和生产成本,Livox将激光雷达技术集成到更多产品和应用之中,从而为自动驾驶、智慧…

数据挖掘丨轻松应用RapidMiner机器学习内置数据分析案例模板详解(下篇)

RapidMiner 案例模板 RapidMiner 机器学习平台提供了一个可视化的操作界面,允许用户通过拖放的方式构建数据分析流程。RapidMiner目前内置了 13 种案例模板,这些模板是预定义的数据分析流程,可以帮助用户快速启动和执行常见的数据分析任务。 …

AI “黏土画风”轻松拿捏,手把手带你云端部署 ComfyUI

作者:鸥弋、筱姜 AI 绘画领域,Stable Diffusion WebUI、Midjourney 、DALL-E 都聚拢了一大批的应用开发者和艺术创作者。ComfyUI 出现时间略晚,但是它让创作者通过工作流的方式,实现自动化水平更高的 AI 生图流程,一面…

ISO17025认证是什么?怎么做?

ISO17025认证是一种国际通用的实验室质量管理体系认证,其目标是确保实验室的技术能力、管理水平以及测试结果的可靠性和准确性达到国际认可的标准。该认证由国际标准化组织(ISO)和国际电工委员会(IEC)联合发布&#xf…

不停“整活”的零食很忙,怎么就跨入万店时代了?

6月12日,合并后的零食很忙、赵一鸣零食宣布,全国门店总数已突破10000家。同时,集团名称也变更为鸣鸣很忙集团。根据第三方机构弗若斯特沙利文认证,鸣鸣很忙集团全国门店数位居零食连锁行业第一。 在此之前,尽管零食很…

Photoshop 2024 mac/win版:探索图像处理的全新境界

Photoshop 2024是Adobe推出的最新图像处理与设计软件,它在继承了前作所有优秀特性的基础上,实现了多个方面的质的飞跃。这款软件凭借其卓越的图像处理性能、丰富的创意工具以及精确的选区编辑功能,成为了图像处理领域的佼佼者。 Photoshop 2…

Spring的循环依赖

循环依赖概述 循环依赖其实也很好理解,可以将这个词拆分成两部分,一个是循环,一个是依赖。循环,顾名思义就是指形成了一个闭合环路,也就是闭环。依赖就是指某个事件的发生要依赖另一个事件。 在Spring中的循环依赖就…

CTFHUB-SQL注入-Cookie注入

由于本关是cookie注入,就不浪费时间判断注入了,在该页面使用 burp工具 抓包,修改cookie后面,加上SQL语句,关掉burp抓包,就可以在题目页面显示结果了 判断字段数量 发现字段数量是2列 使用id-1 union sele…

NewStarCTF_RE(week1,2)

[NewStarCTF 2023 公开赛道]easy_RE ida 可能会把 一个数组或字符串拆开,可以通过计算地址,知道是一起的 也有的会藏在汇编窗口 Segments IDA的Segments窗口 :shiftf7 https://www.cnblogs.com/sch01ar/p/9477697.html ida 各种窗口也是需要…

Prometheus监控系统

目录 一、Prometheus简介 1.Prometheus概念 ①Prometheus概念 ②监控通知流程 ③监控系统的数据产生流程 ④zabbix和prometheus 区别 ⑤TSDB 作为 Prometheus 的存储引擎完美契合了监控数据的应用场景 2.Prometheus的基础组件 ①如何采集数据exporter组件 …

PgSQL技术内幕 - psql与服务端连接与交互机制

PgSQL技术内幕 - 客户端psql与服务端连接与交互机制 简单来说,PgSQL的psql客户端向服务端发起连接请求,服务端接收到请求后,fork出一个子进程,之后由该子进程和客户端进行交互,处理客户端的SQL等,并将结果返…

Python第二语言(八、Python包)

目录 1. 什么是Python包 2. 创包步骤 2.1 new包 2.2 查看创建的包 2.3 拖动文件到包下 3. 导入包 4. 安装第三方包 4.1 什么是第三方包 4.2 安装第三方包-pip 4.3 pip网络优化 1. 什么是Python包 包下有__init__.py就是包,无__init__.py就是文件夹。于Ja…

Linux常用基本命令-操作

目录 一、shell 1、什么是shell 二、Linux基本的命令分类 1、内部命令和外部命令 2、查看内部命令 2.1、help命令 2.2、enable 命令 2.3、type命令 2.4、whereis命令 2.5、which 命令 2.6、hash缓存 ​编辑 三、Linux常用命令 1、Linux命令格式 2、编辑Linux命…

FRP 内网穿透 | 实现远程访问与安全管理

唠唠闲话 内网穿透简介 在互联网上,两个不同主机进行通信需要知道对方的 IP 地址。由于世界人口和设备众多,IPv4 资源相对紧缺,因此绝大部分情况下是通过路由器或交换机转换公网 IP 后才上网。 位于路由器或交换机后的设备通常是内网设备&…

Chroium 源码目录结构分析(1):源码目录体积一栏

获取源码 首先,我们拉一份最新的源代码(笔者是2024.6.6日拉取的): fetch --nohistory chromium 源码预处理 如果运行build,会生成许多生成的代码,因此我们不运行build。 然后,把干扰后续分析…