SqlAlchemy 2.0 中文文档(十五)(3)

简介: SqlAlchemy 2.0 中文文档(十五)

SqlAlchemy 2.0 中文文档(十五)(2)https://developer.aliyun.com/article/1562980


加入到子查询的关联路径

在上一节中说明的子查询形式可以使用relationship()绑定属性更具体地表示,使用使用 Relationship 在别名目标之间进行连接中指示的形式之一。例如,要创建相同的连接,同时确保连接是沿着特定relationship()进行的,我们可以使用PropComparator.of_type()方法,传递包含连接目标的aliased() 构造,该目标是Subquery对象的。

>>> address_subq = aliased(Address, subq, name="address")
>>> stmt = select(User, address_subq).join(User.addresses.of_type(address_subq))
>>> for row in session.execute(stmt):
...     print(f"{row.User} {row.address}")
SELECT  user_account.id,  user_account.name,  user_account.fullname,
anon_1.id  AS  id_1,  anon_1.user_id,  anon_1.email_address
FROM  user_account
JOIN  (SELECT  address.id  AS  id,
address.user_id  AS  user_id,  address.email_address  AS  email_address
FROM  address
WHERE  address.email_address  =  ?)  AS  anon_1  ON  user_account.id  =  anon_1.user_id
[...]  ('pat999@aol.com',)
User(id=3, name='patrick', fullname='Patrick Star') Address(id=4, email_address='pat999@aol.com')

引用多个实体的子查询

包含跨越多个 ORM 实体列的子查询可以一次应用于多个aliased() 构造,并在同一Select构造中针对每个实体分别使用。然而,从 ORM / Python 的角度来看,渲染的 SQL 将继续将所有这些aliased() 构造视为相同的子查询,但可以通过使用适当的aliased() 构造引用不同的返回值和对象属性。

例如,给定同时引用UserAddress的子查询:

>>> user_address_subq = (
...     select(User.id, User.name, User.fullname, Address.id, Address.email_address)
...     .join_from(User, Address)
...     .where(Address.email_address.in_(["pat999@aol.com", "squirrel@squirrelpower.org"]))
...     .subquery()
... )

我们可以针对UserAddress分别创建对同一对象的aliased() 构造:

>>> user_alias = aliased(User, user_address_subq, name="user")
>>> address_alias = aliased(Address, user_address_subq, name="address")

从两个实体中选择的Select构造将一次渲染子查询,但在结果行上下文中可以同时返回UserAddress类的对象:

>>> stmt = select(user_alias, address_alias).where(user_alias.name == "sandy")
>>> for row in session.execute(stmt):
...     print(f"{row.user} {row.address}")
SELECT  anon_1.id,  anon_1.name,  anon_1.fullname,  anon_1.id_1,  anon_1.email_address
FROM  (SELECT  user_account.id  AS  id,  user_account.name  AS  name,
user_account.fullname  AS  fullname,  address.id  AS  id_1,
address.email_address  AS  email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
WHERE  address.email_address  IN  (?,  ?))  AS  anon_1
WHERE  anon_1.name  =  ?
[...]  ('pat999@aol.com',  'squirrel@squirrelpower.org',  'sandy')
User(id=2, name='sandy', fullname='Sandy Cheeks') Address(id=3, email_address='squirrel@squirrelpower.org')

设置连接中最左侧的 FROM 子句

在当前Select状态的左侧与我们要连接的内容不一致的情况下,可以使用Select.join_from() 方法:

>>> stmt = select(Address).join_from(User, User.addresses).where(User.name == "sandy")
>>> print(stmt)
SELECT  address.id,  address.user_id,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
WHERE  user_account.name  =  :name_1 

Select.join_from() 方法接受两个或三个参数,形式可以是 (, ),或者 (, , [])

>>> stmt = select(Address).join_from(User, Address).where(User.name == "sandy")
>>> print(stmt)
SELECT  address.id,  address.user_id,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
WHERE  user_account.name  =  :name_1 

为了为 SELECT 设置初始的 FROM 子句,以便随后可以使用Select.join(),也可以使用Select.select_from()方法:

>>> stmt = select(Address).select_from(User).join(Address).where(User.name == "sandy")
>>> print(stmt)
SELECT  address.id,  address.user_id,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
WHERE  user_account.name  =  :name_1 

提示

Select.select_from()方法实际上并不决定 FROM 子句中表的顺序。如果语句还引用了引用不同顺序的现有表的Join构造,那么Join构造将优先。当我们使用Select.join()Select.join_from()等方法时,这些方法最终会创建这样一个Join对象。因此,在这种情况下,我们可以看到Select.select_from()的内容被覆盖:

>>> stmt = select(Address).select_from(User).join(Address.user).where(User.name == "sandy")
>>> print(stmt)
SELECT  address.id,  address.user_id,  address.email_address
FROM  address  JOIN  user_account  ON  user_account.id  =  address.user_id
WHERE  user_account.name  =  :name_1 

在上面的例子中,我们看到 FROM 子句是address JOIN user_account,尽管我们首先声明了select_from(User)。由于.join(Address.user)方法调用,该语句最终等同于以下内容:

>>> from sqlalchemy.sql import join
>>>
>>> user_table = User.__table__
>>> address_table = Address.__table__
>>>
>>> j = address_table.join(user_table, user_table.c.id == address_table.c.user_id)
>>> stmt = (
...     select(address_table)
...     .select_from(user_table)
...     .select_from(j)
...     .where(user_table.c.name == "sandy")
... )
>>> print(stmt)
SELECT  address.id,  address.user_id,  address.email_address
FROM  address  JOIN  user_account  ON  user_account.id  =  address.user_id
WHERE  user_account.name  =  :name_1 

上面的Join构造被添加为Select.select_from()列表中的另一个条目,它取代了之前的条目。## 关系 WHERE 运算符

除了在Select.join()Select.join_from()方法中使用relationship()构造之外,relationship()还在帮助构建通常用于 WHERE 子句的 SQL 表达式,使用Select.where()方法。

EXISTS 形式:has() / any()

Exists 构造首次出现在 SQLAlchemy 统一教程 的 EXISTS 子查询 部分。此对象用于在标量子查询中与 SQL EXISTS 关键字一起呈现。relationship() 构造提供了一些辅助方法,可用于生成一些常见的 EXISTS 样式的查询,这些查询涉及关系。

对于一对多关系,例如 User.addresses,可以使用与 user_account 表相关联的 address 表的 EXISTS 来产生一个 PropComparator.any()。此方法接受一个可选的 WHERE 条件来限制子查询匹配的行数:

>>> stmt = select(User.fullname).where(
...     User.addresses.any(Address.email_address == "squirrel@squirrelpower.org")
... )
>>> session.execute(stmt).all()
SELECT  user_account.fullname
FROM  user_account
WHERE  EXISTS  (SELECT  1
FROM  address
WHERE  user_account.id  =  address.user_id  AND  address.email_address  =  ?)
[...]  ('squirrel@squirrelpower.org',)
[('Sandy Cheeks',)]

由于 EXISTS 对于负查找更有效,因此一个常见的查询是定位不存在相关实体的实体。这可以通过短语 ~User.addresses.any() 来简洁地实现,以选择没有相关 Address 行的 User 实体:

>>> stmt = select(User.fullname).where(~User.addresses.any())
>>> session.execute(stmt).all()
SELECT  user_account.fullname
FROM  user_account
WHERE  NOT  (EXISTS  (SELECT  1
FROM  address
WHERE  user_account.id  =  address.user_id))
[...]  ()
[('Eugene H. Krabs',)]

PropComparator.has() 方法的工作方式基本与 PropComparator.any() 相同,不同之处在于它用于多对一关系,例如,如果我们想要定位所有属于 “sandy” 的 Address 对象。

>>> stmt = select(Address.email_address).where(Address.user.has(User.name == "sandy"))
>>> session.execute(stmt).all()
SELECT  address.email_address
FROM  address
WHERE  EXISTS  (SELECT  1
FROM  user_account
WHERE  user_account.id  =  address.user_id  AND  user_account.name  =  ?)
[...]  ('sandy',)
[('sandy@sqlalchemy.org',), ('squirrel@squirrelpower.org',)]
```### 关系实例比较运算符
`relationship()` 绑定属性还提供了一些 SQL 构造实现,这些实现旨在根据相关对象的特定实例来过滤 `relationship()` 绑定属性,该实例可以从给定的 持久化(或不太常见的 分离)对象实例中拆解适当的属性值,并按照目标 `relationship()` 构造 WHERE 条件。
+   **多对一等于比较** - 可以将特定对象实例与多对一关系进行比较,以选择目标实体的外键与给定对象的主键值匹配的行:
    ```py
    >>> user_obj = session.get(User, 1)
    SELECT  ...
    >>> print(select(Address).where(Address.user == user_obj))
    SELECT  address.id,  address.user_id,  address.email_address
    FROM  address
    WHERE  :param_1  =  address.user_id 
    ```
+   **多对一不等于比较** - 也可以使用不等于运算符:
    ```py
    >>> print(select(Address).where(Address.user != user_obj))
    SELECT  address.id,  address.user_id,  address.email_address
    FROM  address
    WHERE  address.user_id  !=  :user_id_1  OR  address.user_id  IS  NULL 
    ```
+   **对象包含在一对多集合中** - 这本质上是“等于”比较的一对多版本,选择主键等于相关对象中外键值的行:
    ```py
    >>> address_obj = session.get(Address, 1)
    SELECT  ...
    >>> print(select(User).where(User.addresses.contains(address_obj)))
    SELECT  user_account.id,  user_account.name,  user_account.fullname
    FROM  user_account
    WHERE  user_account.id  =  :param_1 
    ```
+   **从一对多的角度看,对象有一个特定的父对象** - `with_parent()` 函数生成一个比较,返回被给定父对象引用的行,这本质上与在多对一侧使用 `==` 操作符相同:
    ```py
    >>> from sqlalchemy.orm import with_parent
    >>> print(select(Address).where(with_parent(user_obj, User.addresses)))
    SELECT  address.id,  address.user_id,  address.email_address
    FROM  address
    WHERE  :param_1  =  address.user_id 
    ```## 选择 ORM 实体和属性
`select()` 构造接受 ORM 实体,包括映射类以及表示映射列的类级属性,这些在构建时转换为 ORM 注释 的 `FromClause` 和 `ColumnElement` 元素。
包含 ORM 注释实体的 `Select` 对象通常使用 `Session` 对象执行,而不是使用 `Connection` 对象,以便 ORM 相关功能生效,包括可以返回 ORM 映射对象的实例。直接使用 `Connection` 时,结果行将仅包含列级数据。
### 选择 ORM 实体
下面我们从 `User` 实体中选择,生成一个从 `User` 映射到的映射 `Table` 中选择的 `Select`:
```py
>>> result = session.execute(select(User).order_by(User.id))
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account  ORDER  BY  user_account.id
[...]  () 

在选择 ORM 实体时,实体本身作为具有单个元素的行返回结果,而不是一系列单独的列;例如上面,Result 返回仅具有每行单个元素的 Row 对象,该元素保持一个 User 对象:

>>> result.all()
[(User(id=1, name='spongebob', fullname='Spongebob Squarepants'),),
 (User(id=2, name='sandy', fullname='Sandy Cheeks'),),
 (User(id=3, name='patrick', fullname='Patrick Star'),),
 (User(id=4, name='squidward', fullname='Squidward Tentacles'),),
 (User(id=5, name='ehkrabs', fullname='Eugene H. Krabs'),)]

当选择包含 ORM 实体的单元素行列表时,通常会跳过生成Row对象,并直接接收 ORM 实体。这最容易通过使用Session.scalars()方法执行,而不是使用Session.execute()方法来实现,因此返回一个ScalarResult对象,该对象产生单个元素而不是行:

>>> session.scalars(select(User).order_by(User.id)).all()
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account  ORDER  BY  user_account.id
[...]  ()
[User(id=1, name='spongebob', fullname='Spongebob Squarepants'),
 User(id=2, name='sandy', fullname='Sandy Cheeks'),
 User(id=3, name='patrick', fullname='Patrick Star'),
 User(id=4, name='squidward', fullname='Squidward Tentacles'),
 User(id=5, name='ehkrabs', fullname='Eugene H. Krabs')]

调用Session.scalars()方法相当于调用Session.execute()来接收一个Result对象,然后调用Result.scalars()来接收一个ScalarResult对象。 ### 同时选择多个 ORM 实体

select()函数一次接受任意数量的 ORM 类和/或列表达式,包括可以请求多个 ORM 类。当从多个 ORM 类中选择时,它们在每个结果行中根据其类名命名。在下面的示例中,对UserAddress进行 SELECT 的结果行将以UserAddress的名称引用它们:

>>> stmt = select(User, Address).join(User.addresses).order_by(User.id, Address.id)
>>> for row in session.execute(stmt):
...     print(f"{row.User.name} {row.Address.email_address}")
SELECT  user_account.id,  user_account.name,  user_account.fullname,
address.id  AS  id_1,  address.user_id,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
ORDER  BY  user_account.id,  address.id
[...]  ()
spongebob spongebob@sqlalchemy.org
sandy sandy@sqlalchemy.org
sandy squirrel@squirrelpower.org
patrick pat999@aol.com
squidward stentcl@sqlalchemy.org

如果我们想要在这些实体中的行上分配不同的名称,我们将使用aliased()构造,使用aliased.name参数将它们别名为一个明确的名称:

>>> from sqlalchemy.orm import aliased
>>> user_cls = aliased(User, name="user_cls")
>>> email_cls = aliased(Address, name="email")
>>> stmt = (
...     select(user_cls, email_cls)
...     .join(user_cls.addresses.of_type(email_cls))
...     .order_by(user_cls.id, email_cls.id)
... )
>>> row = session.execute(stmt).first()
SELECT  user_cls.id,  user_cls.name,  user_cls.fullname,
email.id  AS  id_1,  email.user_id,  email.email_address
FROM  user_account  AS  user_cls  JOIN  address  AS  email
ON  user_cls.id  =  email.user_id  ORDER  BY  user_cls.id,  email.id
[...]  ()
>>> print(f"{row.user_cls.name}  {row.email.email_address}")
spongebob spongebob@sqlalchemy.org

上述的别名形式在使用关系连接别名目标之间有进一步讨论。

一个现有的Select构造也可以使用Select.add_columns()方法将 ORM 类和/或列表达式添加到其列子句中。我们也可以使用这种形式生成与上述相同的语句:

>>> stmt = (
...     select(User).join(User.addresses).add_columns(Address).order_by(User.id, Address.id)
... )
>>> print(stmt)
SELECT  user_account.id,  user_account.name,  user_account.fullname,
address.id  AS  id_1,  address.user_id,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
ORDER  BY  user_account.id,  address.id 

选择单个属性

映射类上的属性,如User.nameAddress.email_address,可以像传递给select()Column或其他 SQL 表达式对象一样使用。创建针对特定列的select()将返回Row对象,而不是UserAddress对象那样的实体。每个Row将分别表示每个列:

>>> result = session.execute(
...     select(User.name, Address.email_address)
...     .join(User.addresses)
...     .order_by(User.id, Address.id)
... )
SELECT  user_account.name,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
ORDER  BY  user_account.id,  address.id
[...]  () 

上述语句返回Row对象,具有nameemail_address列,如下所示的运行时演示:

>>> for row in result:
...     print(f"{row.name}  {row.email_address}")
spongebob  spongebob@sqlalchemy.org
sandy  sandy@sqlalchemy.org
sandy  squirrel@squirrelpower.org
patrick  pat999@aol.com
squidward  stentcl@sqlalchemy.org

使用 Bundles 分组选择的属性

Bundle构造是一个可扩展的仅 ORM 构造,允许将列表达式集合分组在结果行中:

>>> from sqlalchemy.orm import Bundle
>>> stmt = select(
...     Bundle("user", User.name, User.fullname),
...     Bundle("email", Address.email_address),
... ).join_from(User, Address)
>>> for row in session.execute(stmt):
...     print(f"{row.user.name} {row.user.fullname} {row.email.email_address}")
SELECT  user_account.name,  user_account.fullname,  address.email_address
FROM  user_account  JOIN  address  ON  user_account.id  =  address.user_id
[...]  ()
spongebob Spongebob Squarepants spongebob@sqlalchemy.org
sandy Sandy Cheeks sandy@sqlalchemy.org
sandy Sandy Cheeks squirrel@squirrelpower.org
patrick Patrick Star pat999@aol.com
squidward Squidward Tentacles stentcl@sqlalchemy.org

Bundle可能对创建轻量级视图和自定义列分组有用。Bundle也可以被子类化以返回替代数据结构;请参阅Bundle.create_row_processor()获取示例。

另请参阅

Bundle

Bundle.create_row_processor() ### 选择 ORM 别名

如在使用别名的教程中所讨论的,要创建 ORM 实体的 SQL 别名是使用针对映射类的aliased()构造实现的:

>>> from sqlalchemy.orm import aliased
>>> u1 = aliased(User)
>>> print(select(u1).order_by(u1.id))
SELECT  user_account_1.id,  user_account_1.name,  user_account_1.fullname
FROM  user_account  AS  user_account_1  ORDER  BY  user_account_1.id 

与使用Table.alias()时一样,SQL 别名是匿名命名的。对于从具有显式名称的行中选择实体的情况,还可以传递aliased.name参数:

>>> from sqlalchemy.orm import aliased
>>> u1 = aliased(User, name="u1")
>>> stmt = select(u1).order_by(u1.id)
>>> row = session.execute(stmt).first()
SELECT  u1.id,  u1.name,  u1.fullname
FROM  user_account  AS  u1  ORDER  BY  u1.id
[...]  ()
>>> print(f"{row.u1.name}")
spongebob

另请参阅

aliased构造在几个用例中都很重要,包括:

  • 利用 ORM 进行子查询;章节从子查询中选择实体和加入子查询进一步讨论了这一点。
  • 控制结果集中实体的名称;参见同时选择多个 ORM 实体的示例。
  • 加入到同一个 ORM 实体多次;参见使用关系连接别名目标之间的示例。### 从文本语句中获取 ORM 结果

ORM 支持从来自其他来源的 SELECT 语句加载实体。典型用例是文本 SELECT 语句,在 SQLAlchemy 中使用text()构造表示。text()构造可以通过有关将加载该语句的 ORM 映射列的信息进行增强;然后可以将其与 ORM 实体本身关联,以便基于此语句加载 ORM 对象。

给定一个文本 SQL 语句,我们希望从中加载:

>>> from sqlalchemy import text
>>> textual_sql = text("SELECT id, name, fullname FROM user_account ORDER BY id")

我们可以通过使用TextClause.columns()方法向语句添加列信息;当调用此方法时,TextClause对象转换为TextualSelect对象,其扮演与Select构造类似的角色。TextClause.columns()方法通常传递Column对象或等效对象,在这种情况下,我们可以直接使用User类上的 ORM 映射属性:

>>> textual_sql = textual_sql.columns(User.id, User.name, User.fullname)

现在我们有一个经过 ORM 配置的 SQL 构造,可以分别加载“id”、“name”和“fullname”列。要将此 SELECT 语句作为完整User实体的来源,我们可以使用Select.from_statement()方法将这些列链接到常规的 ORM 启用的Select构造:

>>> orm_sql = select(User).from_statement(textual_sql)
>>> for user_obj in session.execute(orm_sql).scalars():
...     print(user_obj)
SELECT  id,  name,  fullname  FROM  user_account  ORDER  BY  id
[...]  ()
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
User(id=2, name='sandy', fullname='Sandy Cheeks')
User(id=3, name='patrick', fullname='Patrick Star')
User(id=4, name='squidward', fullname='Squidward Tentacles')
User(id=5, name='ehkrabs', fullname='Eugene H. Krabs')

相同的TextualSelect对象也可以使用TextualSelect.subquery()方法转换为子查询,并使用aliased()构造将其链接到User实体中,方式与下文中从子查询中选择实体中所讨论的类似:

>>> orm_subquery = aliased(User, textual_sql.subquery())
>>> stmt = select(orm_subquery)
>>> for user_obj in session.execute(stmt).scalars():
...     print(user_obj)
SELECT  anon_1.id,  anon_1.name,  anon_1.fullname
FROM  (SELECT  id,  name,  fullname  FROM  user_account  ORDER  BY  id)  AS  anon_1
[...]  ()
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
User(id=2, name='sandy', fullname='Sandy Cheeks')
User(id=3, name='patrick', fullname='Patrick Star')
User(id=4, name='squidward', fullname='Squidward Tentacles')
User(id=5, name='ehkrabs', fullname='Eugene H. Krabs')

直接使用TextualSelectSelect.from_statement()与使用aliased()之间的区别在于,在前一种情况下,结果 SQL 中不会生成子查询。在某些情况下,从性能或复杂性的角度来看,这可能是有利的。### 从子查询中选择实体

前一节讨论的aliased()构造可以与任何Subquery构造一起使用,该构造来自诸如Select.subquery()之类的方法,以将 ORM 实体链接到该子查询返回的列;子查询返回的列与实体映射的列之间必须存在列对应关系,这意味着子查询最终需要源自这些实体,就像下面的示例中所示:

>>> inner_stmt = select(User).where(User.id < 7).order_by(User.id)
>>> subq = inner_stmt.subquery()
>>> aliased_user = aliased(User, subq)
>>> stmt = select(aliased_user)
>>> for user_obj in session.execute(stmt).scalars():
...     print(user_obj)
  SELECT  anon_1.id,  anon_1.name,  anon_1.fullname
FROM  (SELECT  user_account.id  AS  id,  user_account.name  AS  name,  user_account.fullname  AS  fullname
FROM  user_account
WHERE  user_account.id  <  ?  ORDER  BY  user_account.id)  AS  anon_1
[generated  in  ...]  (7,)
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
User(id=2, name='sandy', fullname='Sandy Cheeks')
User(id=3, name='patrick', fullname='Patrick Star')
User(id=4, name='squidward', fullname='Squidward Tentacles')
User(id=5, name='ehkrabs', fullname='Eugene H. Krabs')

也请参见

ORM 实体子查询/CTEs - 在 SQLAlchemy 统一教程中

连接到子查询 ### 从 UNION 和其他集合操作中选择实体

union()union_all() 函数是最常见的集合操作,与其他集合操作(例如 except_()intersect() 等)一起提供了一个称为 CompoundSelect 的对象,该对象由多个由集合操作关键字连接的 Select 构造组成。ORM 实体可以使用 Select.from_statement() 方法从简单的复合选择中选择,如前面在从文本语句中获取 ORM 结果中所示。在此方法中,UNION 语句是将呈现的完整语句,不能在使用 Select.from_statement() 后添加额外的条件:

>>> from sqlalchemy import union_all
>>> u = union_all(
...     select(User).where(User.id < 2), select(User).where(User.id == 3)
... ).order_by(User.id)
>>> stmt = select(User).from_statement(u)
>>> for user_obj in session.execute(stmt).scalars():
...     print(user_obj)
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.id  <  ?  UNION  ALL  SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.id  =  ?  ORDER  BY  id
[generated  in  ...]  (2,  3)
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
User(id=3, name='patrick', fullname='Patrick Star')

在查询中,CompoundSelect 构造可以更灵活地使用,可以通过将其组织成子查询并使用 aliased() 连接到 ORM 实体来进一步修改,如前面在从子查询中选择实体中所示。在下面的示例中,我们首先使用 CompoundSelect.subquery() 创建 UNION ALL 语句的子查询,然后将其打包到 aliased() 构造中,在这里它可以像任何其他映射实体一样在 select() 构造中使用,包括我们可以基于其导出列添加过滤和排序条件:

>>> subq = union_all(
...     select(User).where(User.id < 2), select(User).where(User.id == 3)
... ).subquery()
>>> user_alias = aliased(User, subq)
>>> stmt = select(user_alias).order_by(user_alias.id)
>>> for user_obj in session.execute(stmt).scalars():
...     print(user_obj)
SELECT  anon_1.id,  anon_1.name,  anon_1.fullname
FROM  (SELECT  user_account.id  AS  id,  user_account.name  AS  name,  user_account.fullname  AS  fullname
FROM  user_account
WHERE  user_account.id  <  ?  UNION  ALL  SELECT  user_account.id  AS  id,  user_account.name  AS  name,  user_account.fullname  AS  fullname
FROM  user_account
WHERE  user_account.id  =  ?)  AS  anon_1  ORDER  BY  anon_1.id
[generated  in  ...]  (2,  3)
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
User(id=3, name='patrick', fullname='Patrick Star')

请参阅

从联合中选择 ORM 实体 - 在 SQLAlchemy 统一教程中 ### 选择 ORM 实体

下面我们从 User 实体中进行选择,生成一个从 User 映射到的映射 Table 中进行选择的 Select

>>> result = session.execute(select(User).order_by(User.id))
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account  ORDER  BY  user_account.id
[...]  () 

当从 ORM 实体中进行选择时,实体本身作为包含单个元素的行返回结果,而不是一系列单独的列;例如上面的例子,Result 返回仅具有每行单个元素的 Row 对象,该元素保存一个 User 对象:

>>> result.all()
[(User(id=1, name='spongebob', fullname='Spongebob Squarepants'),),
 (User(id=2, name='sandy', fullname='Sandy Cheeks'),),
 (User(id=3, name='patrick', fullname='Patrick Star'),),
 (User(id=4, name='squidward', fullname='Squidward Tentacles'),),
 (User(id=5, name='ehkrabs', fullname='Eugene H. Krabs'),)]

当选择包含 ORM 实体的单元素行列表时,通常会跳过生成 Row 对象,而是直接接收 ORM 实体。这最容易通过使用 Session.scalars() 方法执行,而不是 Session.execute() 方法来实现,以便返回一个 ScalarResult 对象,该对象产生单个元素而不是行:

>>> session.scalars(select(User).order_by(User.id)).all()
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account  ORDER  BY  user_account.id
[...]  ()
[User(id=1, name='spongebob', fullname='Spongebob Squarepants'),
 User(id=2, name='sandy', fullname='Sandy Cheeks'),
 User(id=3, name='patrick', fullname='Patrick Star'),
 User(id=4, name='squidward', fullname='Squidward Tentacles'),
 User(id=5, name='ehkrabs', fullname='Eugene H. Krabs')]

调用 Session.scalars() 方法相当于调用 Session.execute() 来接收一个 Result 对象,然后调用 Result.scalars() 来接收一个 ScalarResult 对象。


SqlAlchemy 2.0 中文文档(十五)(4)https://developer.aliyun.com/article/1562982

相关文章
|
4月前
|
SQL 数据库 Python
SqlAlchemy 2.0 中文文档(十一)(3)
SqlAlchemy 2.0 中文文档(十一)
43 11
|
4月前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(十七)(2)
SqlAlchemy 2.0 中文文档(十七)
39 4
|
4月前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(十七)(3)
SqlAlchemy 2.0 中文文档(十七)
41 4
|
4月前
|
SQL 关系型数据库 API
SqlAlchemy 2.0 中文文档(十七)(4)
SqlAlchemy 2.0 中文文档(十七)
78 4
|
4月前
|
SQL 测试技术 数据库
SqlAlchemy 2.0 中文文档(十二)(5)
SqlAlchemy 2.0 中文文档(十二)
26 2
|
4月前
|
SQL Python
SqlAlchemy 2.0 中文文档(十五)(5)
SqlAlchemy 2.0 中文文档(十五)
79 1
|
4月前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(十五)(1)
SqlAlchemy 2.0 中文文档(十五)
50 1
|
4月前
|
SQL 测试技术 API
SqlAlchemy 2.0 中文文档(十五)(2)
SqlAlchemy 2.0 中文文档(十五)
89 1
|
4月前
|
SQL 测试技术 知识图谱
SqlAlchemy 2.0 中文文档(十五)(4)
SqlAlchemy 2.0 中文文档(十五)
40 1
|
4月前
|
数据库 Python 容器
SqlAlchemy 2.0 中文文档(十四)(3)
SqlAlchemy 2.0 中文文档(十四)
28 1