SqlAlchemy 2.0 中文文档(一)(3)

简介: SqlAlchemy 2.0 中文文档(一)

SqlAlchemy 2.0 中文文档(一)(2)https://developer.aliyun.com/article/1563202


使用 ORM 会话执行

正如之前提到的,上面的大多数模式和示例也适用于与 ORM 一起使用,因此在这里我们将介绍这种用法,以便随着教程的进行,我们将能够以 Core 和 ORM 一起的方式来说明每个模式。

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

Session与非 ORM 构造一起使用时,它会通过我们提供的 SQL 语句,并且通常不会与Connection直接执行有太大不同,因此我们可以根据我们已经学过的简单文本 SQL 操作来说明它。

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}")
BEGIN  (implicit)
SELECT  x,  y  FROM  some_table  WHERE  y  >  ?  ORDER  BY  x,  y
[...]  (6,)
x: 6  y: 8
x: 9  y: 10
x: 11  y: 12
x: 13  y: 14
ROLLBACK 

上面的示例可以与前一节中发送参数中的示例进行比较 - 我们直接将with engine.connect() as conn的调用替换为with Session(engine) as session,然后像使用Connection.execute()方法一样使用Session.execute()方法。

同样,像Connection一样,Session也具有“边提交边执行”的行为,使用Session.commit()方法,下面通过一个文本 UPDATE 语句来修改一些数据进行说明:

>>> 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()
BEGIN  (implicit)
UPDATE  some_table  SET  y=?  WHERE  x=?
[...]  [(11,  9),  (15,  13)]
COMMIT 

在上面,我们使用绑定参数“executemany”风格的执行方式调用了一个 UPDATE 语句,该语句介绍在发送多个参数中,以“边提交边执行”方式结束了该块。

提示

Session 实际上在结束事务后并不保留 Connection 对象。下次需要对数据库执行 SQL 时,它会从 Engine 获取一个新的 Connection

Session 很显然比那个拥有更多的技巧,然而理解它有一个 Session.execute() 方法,该方法的使用方式与 Connection.execute() 相同,将使我们能够开始后面的示例。

另请参阅

使用会话的基础知识 - 展示了与 Session 对象的基本创建和使用模式。

处理数据库元数据

原文:docs.sqlalchemy.org/en/20/tutorial/metadata.html

随着引擎和 SQL 执行完成,我们准备开始一些 Alchemy。SQLAlchemy Core 和 ORM 的核心元素是 SQL  表达语言,它允许流畅、可组合地构建 SQL 查询。这些查询的基础是代表数据库概念(如表和列)的 Python  对象。这些对象被统称为数据库元数据。

SQLAlchemy 中数据库元数据的最常见基础对象称为MetaDataTableColumn。下面的部分将说明这些对象在 Core 导向风格和 ORM 导向风格中的使用方式。

ORM 读者,请继续关注!

与其他部分一样,Core 用户可以跳过 ORM 部分,但 ORM 用户最好从两个角度熟悉这些对象。这里讨论的Table对象在使用 ORM 时以一种更间接的方式(也是完全 Python 类型化的方式)声明,然而,在 ORM 的配置中仍然有一个Table对象。

使用表对象设置元数据

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

要开始使用 SQLAlchemy 表达语言,我们需要构建Table对象,这些对象表示我们有兴趣使用的所有数据库表。 Table是通过编程方式构建的,可以直接使用Table构造函数,也可以间接地使用 ORM 映射类(稍后在使用 ORM 声明形式定义表元数据中描述)。还有一种选项可以从现有数据库加载一些或全部表信息,称为反射。

无论使用哪种方法,我们始终从一个集合开始,这个集合将是我们放置表的地方,称为 MetaData 对象。这个对象本质上是一个围绕 Python 字典的 外观,该字典存储了一系列以其字符串名称为键的 Table 对象。虽然 ORM 在获取这个集合的位置上提供了一些选项,但我们始终可以选择直接创建一个,看起来像这样:

>>> from sqlalchemy import MetaData
>>> metadata_obj = MetaData()

一旦我们有了 MetaData 对象,我们可以声明一些 Table 对象。本教程将从经典的 SQLAlchemy 教程模型开始,其中有一个名为 user_account 的表,存储着网站的用户,以及一个相关的 address 表,存储着与 user_account 表中的行相关联的电子邮件地址。当完全不使用 ORM Declarative 模型时,我们直接构造每个 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 Python 变量来引用它。

Table 的组件

我们可以观察到,Python 中的 Table 构造与 SQL CREATE TABLE 语句相似;从表名开始,然后列出每个列,其中每个列都有一个名称和一个数据类型。我们上面使用的对象是:

  • Table - 表示数据库表并将自己分配给 MetaData 集合。
  • Column - 表示数据库表中的列,并将自己分配给 Table 对象。Column 通常包括一个字符串名称和一个类型对象。以父 TableColumn 对象的集合通常通过位于 Table.c 的关联数组来访问:
>>> user_table.c.name
Column('name', String(length=30), table=<user_account>)
>>> user_table.c.keys()
['id', 'name', 'fullname']
  • IntegerString - 这些类表示 SQL 数据类型,并且可以被传递给具有或没有必要被实例化的Column。在上面的例子中,我们想要给“name”列一个长度为“30”,因此我们实例化了String(30)。但对于“id”和“fullname”,我们没有指定这些,所以我们可以发送类本身。

另请参阅

MetaDataTableColumn的参考和 API 文档位于用 MetaData 描述数据库。数据类型的参考文档位于 SQL 数据类型对象。

在接下来的一节中,我们将说明Table的一个基本功能,即在特定数据库连接上生成 DDL。但首先,我们将声明第二个Table

声明简单约束

示例中的第一个Columnuser_table中包含Column.primary_key参数,这是一种简写技术,表示这个Column应该是这个表的主键的一部分。主键本身通常是隐式声明的,并且由PrimaryKeyConstraint构造表示,我们可以在Table对象的Table.primary_key属性上看到它:

>>> user_table.primary_key
PrimaryKeyConstraint(Column('id', Integer(), table=<user_account>, primary_key=True, nullable=False))

最常显式声明的约束是对应于数据库外键约束的ForeignKeyConstraint对象。当我们声明相互关联的表时,SQLAlchemy 不仅使用这些外键约束声明在向数据库发送 CREATE 语句时将其发送出去,而且还用于帮助构造 SQL 表达式。

一个涉及目标表上仅一个列的ForeignKeyConstraint通常使用列级别的速记符号通过ForeignKey对象声明。下面我们声明了一个将具有引用user表的外键约束的第二个表address

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

上面的表还包含了第三种约束类型,在 SQL 中是“NOT NULL”约束,在上面使用Column.nullable参数进行指示。

提示

Column定义中使用ForeignKey对象时,我们可以省略该Column的数据类型;它会自动从相关列的数据类型中推断出来,在上面的示例中是user_account.id列的Integer数据类型。

在下一节中,我们将发出useraddress表的完整 DDL 以查看完成的结果。

发出 DDL 到数据库

我们已经构建了一个对象结构,代表数据库中的两个数据库表,在根MetaData对象开始,然后进入两个Table对象,每个对象都包含一组ColumnConstraint对象。这个对象结构将成为我们今后使用 Core 和 ORM 执行的大多数操作的中心。

我们可以对此结构进行的第一个有用的操作是发出 CREATE TABLE 语句,或者 DDL 到我们的 SQLite 数据库,以便我们可以从中插入和查询数据。我们已经拥有完成此操作所需的所有工具,通过在我们的MetaData上调用MetaData.create_all()方法,将目标数据库的Engine传递给它:

>>> metadata_obj.create_all(engine)
BEGIN  (implicit)
PRAGMA  main.table_...info("user_account")
...
PRAGMA  main.table_...info("address")
...
CREATE  TABLE  user_account  (
  id  INTEGER  NOT  NULL,
  name  VARCHAR(30),
  fullname  VARCHAR,
  PRIMARY  KEY  (id)
)
...
CREATE  TABLE  address  (
  id  INTEGER  NOT  NULL,
  user_id  INTEGER  NOT  NULL,
  email_address  VARCHAR  NOT  NULL,
  PRIMARY  KEY  (id),
  FOREIGN  KEY(user_id)  REFERENCES  user_account  (id)
)
...
COMMIT 

以上的 DDL 创建过程包括一些 SQLite 特定的 PRAGMA 语句,用于在发出 CREATE 之前测试每个表的存在性。所有步骤也包含在 BEGIN/COMMIT 对中,以适应事务性 DDL。

create 进程还负责按正确顺序发出 CREATE 语句;以上,FOREIGN KEY 约束依赖于 user 表的存在,因此 address 表在第二创建。在更复杂的依赖情况下,FOREIGN KEY 约束也可能使用 ALTER 在表创建后进行应用。

MetaData 对象还具有一个 MetaData.drop_all() 方法,它将按照与发出 CREATE 相反的顺序发出 DROP 语句以删除模式元素。## 使用 ORM 声明式表单定义表元数据

在使用 ORM 时,声明 Table 元数据的过程通常与声明 映射 类的过程结合在一起。映射类是我们想要创建的任何 Python 类,然后它将具有链接到数据库表中列的属性。虽然有几种实现方式,但最常见的风格称为 声明式,它允许我们一次声明我们的用户定义类和 Table 元数据。

建立声明性基础

在使用 ORM 时,MetaData 集合仍然存在,但它本身与一个仅用于 ORM 的构造关联,通常称为 声明式基础。获取新的声明式基础的最简便方法是创建一个继承 SQLAlchemy DeclarativeBase 类的新类:

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

以上,Base 类就是我们将称为声明式基础的类。当我们创建新的类作为 Base 的子类时,并结合适当的类级指令,它们将在类创建时各自作为一个新的 ORM 映射类 建立,每个类通常(但不一定)引用一个特定的 Table 对象。

Declarative Base 指的是一个MetaData集合,它会自动为我们创建,假设我们没有从外部提供。这个MetaData集合可以通过DeclarativeBase.metadata类级别属性访问。当我们创建新的映射类时,它们将分别引用此MetaData集合内的一个Table

>>> Base.metadata
MetaData()

Declarative Base 还指的是一个称为registry的集合,它是 SQLAlchemy ORM 中的中央“映射器配置”单元。虽然很少直接访问,但该对象在映射器配置过程中是至关重要的,因为一组 ORM 映射类将通过此注册表相互协调。与MetaData的情况一样,我们的 Declarative Base 也为我们创建了一个registry(再次提供自己的registry的选项),我们可以通过DeclarativeBase.registry类变量访问它:

>>> Base.registry
<sqlalchemy.orm.decl_api.registry object at 0x...>
```### 声明映射类
有了`Base`类的设立,我们现在可以根据新类`User`和`Address`定义`user_account`和`address`表的 ORM 映射类。我们下面展示了最现代化的 Declarative 形式,它是从[**PEP 484**](https://peps.python.org/pep-0484/)类型注解中驱动的,使用了一个特殊类型`Mapped`,它指示要映射为特定类型的属性:
```py
>>> 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})"

上面的两个类UserAddress现在被称为ORM 映射类,并且可以在 ORM 持久性和查询操作中使用,稍后将对这些类的详细信息进行描述:

  • 每个类都指向一个Table对象,该对象是作为声明性映射过程的一部分生成的,并通过将字符串赋值给DeclarativeBase.__tablename__属性命名。一旦类被创建,这个生成的Table可以通过DeclarativeBase.__table__属性进行访问。
  • 如前所述,这种形式被称为声明性表配置。数种替代声明风格之一会让我们直接构建Table对象,并直接将其分配给DeclarativeBase.__table__。这种风格被称为声明性与命令式表配置。
  • 为了指示Table中的列,我们使用mapped_column()构造,结合基于Mapped类型的类型注释。此对象将生成应用于Table构造的Column对象。
  • 对于简单数据类型且没有其他选项的列,我们可以单独指定Mapped类型注释,使用简单的 Python 类型如intstr表示IntegerString。在声明性映射过程中,如何解释 Python 类型的定制化是非常开放的;请参阅使用注释的声明性表(用于mapped_column()的类型注释形式)和自定义类型映射部分了解背景知识。
  • 根据Optional[]类型注释(或其等效形式, | NoneUnion[, None])的存在,可以将列声明为“可空”或“非空”。还可以显式使用mapped_column.nullable参数(不必与注释的可选性匹配)。
  • 使用显式类型注释是完全可选的。我们也可以在没有注释的情况下使用mapped_column()。在使用这种形式时,我们会根据需要在每个mapped_column()构造内使用更明确的类型对象,如IntegerString以及nullable=False
  • 另外两个属性,User.addressesAddress.user,定义了一种不同类型的属性,称为relationship(),它具有与注释相似的配置样式。relationship()构造在使用 ORM 相关对象中有更详细的讨论。
  • 如果我们没有声明自己的__init__()方法,则会自动为类添加一个__init__()方法。该方法的默认形式接受所有属性名称作为可选关键字参数:
>>> sandy = User(name="sandy", fullname="Sandy Cheeks")
  • 要自动生成一个全功能的__init__()方法,既提供位置参数又提供具有默认关键字值的参数,可以使用在声明式数据类映射中引入的数据类功能。当然,始终可以选择使用显式的__init__()方法。
  • 添加__repr__()方法是为了获得可读的字符串输出;这些方法不需要存在的要求。与__init__()一样,可以使用 dataclasses 功能自动生成__repr__()方法。

另请参阅

ORM 映射风格 - 不同 ORM 配置风格的完整背景。

声明式映射 - 声明式类映射概述

使用mapped_column()的声明式表 - 详细说明如何使用mapped_column()Mapped来定义在使用声明式时要映射的Table中的列。

从 ORM 映射向数据库发出 DDL

由于我们的 ORM 映射类引用包含在MetaData集合中的Table对象,所以根据声明式基类发出 DDL 与在 Emitting DDL to the Database 中描述的过程相同。在我们的情况下,我们已经在我们的 SQLite 数据库中生成了useraddress表。如果我们之前没有这样做,我们可以自由地利用与我们的 ORM 声明基类相关联的MetaData来做到这一点,方法是通过访问DeclarativeBase.metadata属性中的集合,然后像以前一样使用MetaData.create_all()。在这种情况下,会运行 PRAGMA 语句,但不会生成新表,因为已经发现它们已经存在:

>>> Base.metadata.create_all(engine)
BEGIN  (implicit)
PRAGMA  main.table_...info("user_account")
...
PRAGMA  main.table_...info("address")
...
COMMIT 
```## 表反射
为了完成与表元数据一起工作的部分,我们将说明在该部分开头提到的另一个操作,即**表反射**。表反射是指通过读取数据库的当前状态来生成`Table`和相关对象的过程。而在之前的部分中,我们一直在 Python 中声明`Table`对象,然后有选择地将 DDL 发出到数据库以生成这样的模式,反射过程将这两个步骤反向执行,从现有数据库开始,并生成用于表示该数据库中模式的 Python 数据结构。
提示
并非要求必须使用反射才能将 SQLAlchemy 与现有数据库一起使用。完全可以在 Python 中显式声明所有元数据,使其结构与现有数据库相对应,这是很典型的。元数据结构也不必包含表、列或其他在本地应用程序中不需要的预先存在数据库中的约束和构造。
作为反射的示例,我们将创建一个新的`Table`对象,该对象表示我们在本文档的前几节中手动创建的`some_table`对象。这样做的方式有很多种,但最基本的方式是构建一个`Table`对象,给定表的名称和它将属于的`MetaData`集合,然后不是指示单独的`Column`和`Constraint`对象,而是传递目标`Engine`使用`Table.autoload_with`参数:
```py
>>> some_table = Table("some_table", metadata_obj, autoload_with=engine)
BEGIN  (implicit)
PRAGMA  main.table_...info("some_table")
[raw  sql]  ()
SELECT  sql  FROM  (SELECT  *  FROM  sqlite_master  UNION  ALL  SELECT  *  FROM  sqlite_temp_master)  WHERE  name  =  ?  AND  type  in  ('table',  'view')
[raw  sql]  ('some_table',)
PRAGMA  main.foreign_key_list("some_table")
...
PRAGMA  main.index_list("some_table")
...
ROLLBACK 

在这个过程的结尾,some_table对象现在包含了表中存在的Column对象的信息,该对象可与我们明确声明的Table完全相同的方式使用:

>>> some_table
Table('some_table', MetaData(),
 Column('x', INTEGER(), table=<some_table>),
 Column('y', INTEGER(), table=<some_table>),
 schema=None)

另请参阅

了解有关表和模式反射的更多信息,请参阅反射数据库对象。

对于 ORM 相关的表反射变体,在使用反射表声明映射一节中包含了可用选项的概述。

下一步

现在我们有一个 SQLite 数据库准备好使用,其中有两个表存在,并且我们可以使用Connection和/或 ORM Session通过 Core 和 ORM 表导向的构造与这些表进行交互。在接下来的章节中,我们将说明如何使用这些结构创建、操作和选择数据。


SqlAlchemy 2.0 中文文档(一)(4)https://developer.aliyun.com/article/1563204

相关文章
|
5月前
|
测试技术 API 数据库
SqlAlchemy 2.0 中文文档(九)(5)
SqlAlchemy 2.0 中文文档(九)
35 0
|
5月前
|
SQL JSON 关系型数据库
SqlAlchemy 2.0 中文文档(五)(5)
SqlAlchemy 2.0 中文文档(五)
64 4
|
5月前
|
存储 SQL API
SqlAlchemy 2.0 中文文档(四)(5)
SqlAlchemy 2.0 中文文档(四)
41 3
|
5月前
|
SQL 测试技术 Python
SqlAlchemy 2.0 中文文档(四)(4)
SqlAlchemy 2.0 中文文档(四)
67 3
|
5月前
|
SQL 存储 数据库
SqlAlchemy 2.0 中文文档(一)(4)
SqlAlchemy 2.0 中文文档(一)
86 1
|
5月前
|
SQL API 数据库
SqlAlchemy 2.0 中文文档(一)(5)
SqlAlchemy 2.0 中文文档(一)
129 1
|
5月前
|
SQL 数据库 Python
SqlAlchemy 2.0 中文文档(十)(3)
SqlAlchemy 2.0 中文文档(十)
39 1
|
5月前
|
SQL 关系型数据库 测试技术
SqlAlchemy 2.0 中文文档(十)(1)
SqlAlchemy 2.0 中文文档(十)
38 1
|
5月前
|
SQL 存储 API
SqlAlchemy 2.0 中文文档(十)(5)
SqlAlchemy 2.0 中文文档(十)
35 1
|
5月前
|
SQL API 数据库
SqlAlchemy 2.0 中文文档(四)(1)
SqlAlchemy 2.0 中文文档(四)
44 1