SqlAlchemy 2.0 中文文档(十二)(1)

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: SqlAlchemy 2.0 中文文档(十二)


原文:docs.sqlalchemy.org/en/20/contents.html

邻接列表关系

原文:docs.sqlalchemy.org/en/20/orm/self_referential.html

邻接列表模式是一种常见的关系模式,其中表包含对自身的外键引用,换句话说是自引用关系。这是在平面表中表示层次数据的最常见方法。其他方法包括嵌套集,有时称为“修改的先序”,以及材料路径。尽管在 SQL 查询中评估其流畅性时修改的先序具有吸引力,但邻接列表模型可能是满足大多数层次存储需求的最合适模式,原因是并发性、减少的复杂性,以及修改的先序对于能够完全加载子树到应用程序空间的应用程序几乎没有优势。

另请参阅

此部分详细说明了自引用关系的单表版本。有关使用第二个表作为关联表的自引用关系,请参阅自引用多对多关系部分。

在本示例中,我们将使用一个名为Node的单个映射类,表示树结构:

class Node(Base):
    __tablename__ = "node"
    id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer, ForeignKey("node.id"))
    data = mapped_column(String(50))
    children = relationship("Node")

使用此结构,可以构建如下的图形:

root --+---> child1
       +---> child2 --+--> subchild1
       |              +--> subchild2
       +---> child3

可以用数据表示为:

id       parent_id     data
---      -------       ----
1        NULL          root
2        1             child1
3        1             child2
4        3             subchild1
5        3             subchild2
6        1             child3

这里的relationship()配置与“正常”的一对多关系的工作方式相同,唯一的例外是,“方向”,即关系是一对多还是多对一,默认假定为一对多。要将关系建立为多对一,需要添加一个额外的指令,称为relationship.remote_side,它是一个Column或一组Column对象,指示应该被视为“远程”的对象:

class Node(Base):
    __tablename__ = "node"
    id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer, ForeignKey("node.id"))
    data = mapped_column(String(50))
    parent = relationship("Node", remote_side=[id])

在上述情况下,id列被应用为parent relationship()relationship.remote_side,从而将parent_id建立为“本地”端,并且关系随后表现为多对一。

一如既往,两个方向可以结合成一个双向关系,使用两个由relationship.back_populates链接的relationship()构造。

class Node(Base):
    __tablename__ = "node"
    id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer, ForeignKey("node.id"))
    data = mapped_column(String(50))
    children = relationship("Node", back_populates="parent")
    parent = relationship("Node", back_populates="children", remote_side=[id])

另请参阅

邻接列表 - 更新为 SQLAlchemy 2.0 的工作示例

复合邻接列表

邻接列表关系的一个子类别是在连接条件的“本地”和“远程”两侧都存在特定列的罕见情况。下面是Folder类的一个示例;使用复合主键,account_id列指向自身,以指示位于与父文件夹相同帐户内的子文件夹;而folder_id则指向该帐户内的特定文件夹:

class Folder(Base):
    __tablename__ = "folder"
    __table_args__ = (
        ForeignKeyConstraint(
            ["account_id", "parent_id"], ["folder.account_id", "folder.folder_id"]
        ),
    )
    account_id = mapped_column(Integer, primary_key=True)
    folder_id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer)
    name = mapped_column(String)
    parent_folder = relationship(
        "Folder", back_populates="child_folders", remote_side=[account_id, folder_id]
    )
    child_folders = relationship("Folder", back_populates="parent_folder")

在上述示例中,我们将account_id传递到relationship.remote_side列表中。relationship()识别到这里的account_id列在两侧都存在,并将“远程”列与它识别为唯一存在于“远程”侧的folder_id列对齐。

自引用查询策略

查询自引用结构的方式与任何其他查询相同:

# get all nodes named 'child2'
session.scalars(select(Node).where(Node.data == "child2"))

但是,当尝试沿着树的一个级别从一个外键连接到下一个级别时,需要额外小心。在 SQL 中,从表连接到自身的连接需要至少对表达式的一侧进行“别名”,以便可以明确引用它。

请回想一下 ORM 教程中的选择 ORM 别名,aliased()结构通常用于提供 ORM 实体的“别名”。使用此技术从Node连接到自身的连接如下所示:

from sqlalchemy.orm import aliased
nodealias = aliased(Node)
session.scalars(
    select(Node)
    .where(Node.data == "subchild1")
    .join(Node.parent.of_type(nodealias))
    .where(nodealias.data == "child2")
).all()
SELECT  node.id  AS  node_id,
  node.parent_id  AS  node_parent_id,
  node.data  AS  node_data
FROM  node  JOIN  node  AS  node_1
  ON  node.parent_id  =  node_1.id
WHERE  node.data  =  ?
  AND  node_1.data  =  ?
['subchild1',  'child2'] 
```## 配置自引用的急切加载
在正常查询操作期间,通过从父表到子表的连接或外连接来发生关系的急切加载,以便可以从单个 SQL 语句或所有子集合的第二个语句中填充父对象及其直接子集合或引用。SQLAlchemy 的连接和子查询急切加载在连接到相关项时在所有情况下使用别名表,因此与自引用连接兼容。然而,要使用自引用关系进行急切加载,SQLAlchemy 需要告知应该连接和/或查询多少级深度;否则,急切加载将根本不会发生。此深度设置通过`relationships.join_depth`进行配置:
```py
class Node(Base):
    __tablename__ = "node"
    id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer, ForeignKey("node.id"))
    data = mapped_column(String(50))
    children = relationship("Node", lazy="joined", join_depth=2)
session.scalars(select(Node)).all()
SELECT  node_1.id  AS  node_1_id,
  node_1.parent_id  AS  node_1_parent_id,
  node_1.data  AS  node_1_data,
  node_2.id  AS  node_2_id,
  node_2.parent_id  AS  node_2_parent_id,
  node_2.data  AS  node_2_data,
  node.id  AS  node_id,
  node.parent_id  AS  node_parent_id,
  node.data  AS  node_data
FROM  node
  LEFT  OUTER  JOIN  node  AS  node_2
  ON  node.id  =  node_2.parent_id
  LEFT  OUTER  JOIN  node  AS  node_1
  ON  node_2.id  =  node_1.parent_id
[] 

复合邻接列表

邻接列表关系的一个子类别是在连接条件的“本地”和“远程”两侧都存在特定列的罕见情况。下面是Folder类的一个示例;使用复合主键,account_id列指向自身,以指示位于与父文件夹相同帐户内的子文件夹;而folder_id则指向该帐户内的特定文件夹:

class Folder(Base):
    __tablename__ = "folder"
    __table_args__ = (
        ForeignKeyConstraint(
            ["account_id", "parent_id"], ["folder.account_id", "folder.folder_id"]
        ),
    )
    account_id = mapped_column(Integer, primary_key=True)
    folder_id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer)
    name = mapped_column(String)
    parent_folder = relationship(
        "Folder", back_populates="child_folders", remote_side=[account_id, folder_id]
    )
    child_folders = relationship("Folder", back_populates="parent_folder")

在上面的例子中,我们将account_id传递到relationship.remote_side列表中。relationship()识别到这里的account_id列在两侧均存在,并且将“远程”列与它识别为唯一存在于“远程”一侧的folder_id列对齐。

自引用查询策略

自引用结构的查询与任何其他查询相同:

# get all nodes named 'child2'
session.scalars(select(Node).where(Node.data == "child2"))

但是,在尝试从树的一级到下一级进行连接时需要特别注意。在 SQL 中,从表连接到自身需要至少一个表达式的一侧被“别名”,以便可以明确地引用它。

请回想一下在 ORM 教程中选择 ORM 别名,aliased()构造通常用于提供 ORM 实体的“别名”。使用这种技术从Node到自身的连接看起来像:

from sqlalchemy.orm import aliased
nodealias = aliased(Node)
session.scalars(
    select(Node)
    .where(Node.data == "subchild1")
    .join(Node.parent.of_type(nodealias))
    .where(nodealias.data == "child2")
).all()
SELECT  node.id  AS  node_id,
  node.parent_id  AS  node_parent_id,
  node.data  AS  node_data
FROM  node  JOIN  node  AS  node_1
  ON  node.parent_id  =  node_1.id
WHERE  node.data  =  ?
  AND  node_1.data  =  ?
['subchild1',  'child2'] 

配置自引用关系的急切加载

通过在正常查询操作期间从父表到子表使用连接或外连接来进行关系的急切加载,以便可以从单个 SQL  语句或所有直接子集合的第二个语句中填充父表及其直接子集合或引用。SQLAlchemy  的连接和子查询急切加载在加入相关项时始终使用别名表,因此与自引用连接兼容。然而,要想使用自引用关系的急切加载,需要告诉 SQLAlchemy  应该加入和/或查询多少级深度;否则,急切加载将根本不会发生。此深度设置通过relationships.join_depth进行配置:

class Node(Base):
    __tablename__ = "node"
    id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer, ForeignKey("node.id"))
    data = mapped_column(String(50))
    children = relationship("Node", lazy="joined", join_depth=2)
session.scalars(select(Node)).all()
SELECT  node_1.id  AS  node_1_id,
  node_1.parent_id  AS  node_1_parent_id,
  node_1.data  AS  node_1_data,
  node_2.id  AS  node_2_id,
  node_2.parent_id  AS  node_2_parent_id,
  node_2.data  AS  node_2_data,
  node.id  AS  node_id,
  node.parent_id  AS  node_parent_id,
  node.data  AS  node_data
FROM  node
  LEFT  OUTER  JOIN  node  AS  node_2
  ON  node.id  =  node_2.parent_id
  LEFT  OUTER  JOIN  node  AS  node_1
  ON  node_2.id  =  node_1.parent_id
[] 

配置关系连接

原文:docs.sqlalchemy.org/en/20/orm/join_conditions.html

relationship()通常会通过检查两个表之间的外键关系来创建两个表之间的连接,以确定应该比较哪些列。有各种情况需要定制此行为。

处理多个连接路径

处理的最常见情况之一是两个表之间存在多个外键路径时。

考虑一个包含两个外键到Address类的Customer类:

from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
    pass
class Customer(Base):
    __tablename__ = "customer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)
    billing_address_id = mapped_column(Integer, ForeignKey("address.id"))
    shipping_address_id = mapped_column(Integer, ForeignKey("address.id"))
    billing_address = relationship("Address")
    shipping_address = relationship("Address")
class Address(Base):
    __tablename__ = "address"
    id = mapped_column(Integer, primary_key=True)
    street = mapped_column(String)
    city = mapped_column(String)
    state = mapped_column(String)
    zip = mapped_column(String)

当我们尝试使用上述映射时,将产生错误:

sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join
condition between parent/child tables on relationship
Customer.billing_address - there are multiple foreign key
paths linking the tables.  Specify the 'foreign_keys' argument,
providing a list of those columns which should be
counted as containing a foreign key reference to the parent table.

上述消息相当长。relationship()可以返回许多潜在消息,这些消息已经被精心设计用于检测各种常见的配置问题;大多数都会建议需要哪些额外配置来解决模糊或其他缺失的信息。

在这种情况下,消息希望我们通过为每个指定的relationship()指定应该考虑哪个外键列来修饰每一个,并且适当的形式如下:

class Customer(Base):
    __tablename__ = "customer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)
    billing_address_id = mapped_column(Integer, ForeignKey("address.id"))
    shipping_address_id = mapped_column(Integer, ForeignKey("address.id"))
    billing_address = relationship("Address", foreign_keys=[billing_address_id])
    shipping_address = relationship("Address", foreign_keys=[shipping_address_id])

在上面,我们指定了foreign_keys参数,它是一个ColumnColumn对象的列表,指示要考虑的“外键”列,或者换句话说,包含引用父表的值的列。从Customer对象加载Customer.billing_address关系将使用billing_address_id中的值来标识要加载的Address行;类似地,shipping_address_id用于shipping_address关系。这两列的关联在持久性期间也起到了作用;刚刚插入的Address对象的新生成的主键将在刷新期间复制到关联的Customer对象的适当外键列中。

在使用 Declarative 指定foreign_keys时,我们还可以使用字符串名称进行指定,但是如果使用列表,列表应该是字符串的一部分是很重要的:

billing_address = relationship("Address", foreign_keys="[Customer.billing_address_id]")

在这个具体的例子中,在任何情况下列表都是不必要的,因为我们只需要一个Column

billing_address = relationship("Address", foreign_keys="Customer.billing_address_id")

警告

当作为 Python 可执行字符串传递时,relationship.foreign_keys 参数将使用 Python 的 eval() 函数进行解释。请勿将不受信任的输入传递给此字符串。详细信息请参见关系参数的评估。

relationship() 在构建连接时的默认行为是将一侧的主键列的值等同于另一侧的外键引用列的值。我们可以使用 relationship.primaryjoin 参数来更改此条件,以及在使用“次要”表时,还可以使用 relationship.secondaryjoin 参数。

在下面的示例中,我们使用 User 类以及存储街道地址的 Address 类来创建一个关系 boston_addresses,它将仅加载指定城市为“波士顿”的 Address 对象:

from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
    pass
class User(Base):
    __tablename__ = "user"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)
    boston_addresses = relationship(
        "Address",
        primaryjoin="and_(User.id==Address.user_id, Address.city=='Boston')",
    )
class Address(Base):
    __tablename__ = "address"
    id = mapped_column(Integer, primary_key=True)
    user_id = mapped_column(Integer, ForeignKey("user.id"))
    street = mapped_column(String)
    city = mapped_column(String)
    state = mapped_column(String)
    zip = mapped_column(String)

在此字符串 SQL 表达式中,我们使用了 and_() 连接构造来为连接条件建立两个不同的谓词 - 将 User.idAddress.user_id 列相互连接,同时将 Address 中的行限制为只有 city='Boston'。在使用声明式时,诸如 and_() 这样的基本 SQL 函数会自动在字符串 relationship() 参数的评估命名空间中可用。

警告

当作为 Python 可执行字符串传递时,relationship.primaryjoin 参数将使用 Python 的 eval() 函数进行解释。请勿将不受信任的输入传递给此字符串。详细信息请参见关系参数的评估。

我们在relationship.primaryjoin中使用的自定义标准通常只在 SQLAlchemy 渲染 SQL 以加载或表示此关系时才重要。也就是说,它用于在执行每个属性的延迟加载时发出的 SQL 语句中,或者在查询时构造联接时,例如通过Select.join()或通过渴望的“joined”或“subquery”加载样式。当操作内存中的对象时,我们可以将任何我们想要的Address对象放入boston_addresses集合中,而不管.city属性的值是什么。这些对象将一直保留在集合中,直到属性过期并重新从应用准则的数据库中加载为止。当发生刷新时,boston_addresses内的对象将被无条件地刷新,将主键user.id列的值分配给每一行的持有外键的address.user_id列。在这里,city标准没有影响,因为刷新过程只关心将主键值同步到引用外键值中。## 创建自定义外键条件

主要连接条件的另一个元素是如何确定那些被认为是“外部”的列的。通常,一些Column对象的子集将指定ForeignKey,或者否则将是与连接条件相关的ForeignKeyConstraint的一部分。relationship()查找此外键状态,因为它决定了它应该如何加载和持久化此关系的数据。然而,relationship.primaryjoin参数可以用来创建不涉及任何“架构”级外键的连接条件。我们可以结合relationship.primaryjoin以及relationship.foreign_keysrelationship.remote_side显式地建立这样一个连接。

下面,一个名为HostEntry的类与自身连接,将字符串content列与ip_address列相等,后者是一种名为INET的 PostgreSQL 类型。我们需要使用cast()来将连接的一侧转换为另一侧的类型:

from sqlalchemy import cast, String, Column, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import INET
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
    pass
class HostEntry(Base):
    __tablename__ = "host_entry"
    id = mapped_column(Integer, primary_key=True)
    ip_address = mapped_column(INET)
    content = mapped_column(String(50))
    # relationship() using explicit foreign_keys, remote_side
    parent_host = relationship(
        "HostEntry",
        primaryjoin=ip_address == cast(content, INET),
        foreign_keys=content,
        remote_side=ip_address,
    )

上述关系将产生如下连接:

SELECT  host_entry.id,  host_entry.ip_address,  host_entry.content
FROM  host_entry  JOIN  host_entry  AS  host_entry_1
ON  host_entry_1.ip_address  =  CAST(host_entry.content  AS  INET)

上述的另一种替代语法是在relationship.primaryjoin表达式内联使用foreign()remote()注释。此语法表示了relationship()通常自动应用于连接条件的注释,给定了relationship.foreign_keysrelationship.remote_side参数。当存在显式连接条件时,这些函数可能更加简洁,并且还标记了“外键”或“远程”列的确切位置,无论该列是否多次声明或在复杂的 SQL 表达式中声明:

from sqlalchemy.orm import foreign, remote
class HostEntry(Base):
    __tablename__ = "host_entry"
    id = mapped_column(Integer, primary_key=True)
    ip_address = mapped_column(INET)
    content = mapped_column(String(50))
    # relationship() using explicit foreign() and remote() annotations
    # in lieu of separate arguments
    parent_host = relationship(
        "HostEntry",
        primaryjoin=remote(ip_address) == cast(foreign(content), INET),
    )
```## 在连接条件中使用自定义运算符
关于关系的另一个用例是使用自定义运算符,例如在与`INET`和`CIDR`等类型结合时,使用 PostgreSQL 的“包含于”`<<`运算符。对于自定义布尔运算符,我们使用`Operators.bool_op()`函数:
```py
inet_column.bool_op("<<")(cidr_column)

类似上述的比较可以直接在构建relationship()时,与relationship.primaryjoin一起使用:

class IPA(Base):
    __tablename__ = "ip_address"
    id = mapped_column(Integer, primary_key=True)
    v4address = mapped_column(INET)
    network = relationship(
        "Network",
        primaryjoin="IPA.v4address.bool_op('<<')(foreign(Network.v4representation))",
        viewonly=True,
    )
class Network(Base):
    __tablename__ = "network"
    id = mapped_column(Integer, primary_key=True)
    v4representation = mapped_column(CIDR)

上面的查询如下:

select(IPA).join(IPA.network

将呈现为:

SELECT  ip_address.id  AS  ip_address_id,  ip_address.v4address  AS  ip_address_v4address
FROM  ip_address  JOIN  network  ON  ip_address.v4address  <<  network.v4representation
```## 基于 SQL 函数的自定义运算符
`Operators.op.is_comparison`的用例的一种变体是当我们不使用运算符,而是使用 SQL 函数时。这种用例的典型示例是 PostgreSQL PostGIS 函数,但任何数据库中解析为二进制条件的 SQL 函数都可以应用。为了适应这种用例,`FunctionElement.as_comparison()`方法可以修改任何 SQL 函数,例如从`func`命名空间调用的函数,以指示 ORM 函数生成两个表达式的比较。下面的示例用[Geoalchemy2](https://geoalchemy-2.readthedocs.io/)库说明了这一点:
```py
from geoalchemy2 import Geometry
from sqlalchemy import Column, Integer, func
from sqlalchemy.orm import relationship, foreign
class Polygon(Base):
    __tablename__ = "polygon"
    id = mapped_column(Integer, primary_key=True)
    geom = mapped_column(Geometry("POLYGON", srid=4326))
    points = relationship(
        "Point",
        primaryjoin="func.ST_Contains(foreign(Polygon.geom), Point.geom).as_comparison(1, 2)",
        viewonly=True,
    )
class Point(Base):
    __tablename__ = "point"
    id = mapped_column(Integer, primary_key=True)
    geom = mapped_column(Geometry("POINT", srid=4326))

在上面,FunctionElement.as_comparison()表明func.ST_Contains() SQL 函数正在比较Polygon.geomPoint.geom表达式。foreign()注释另外指出了在此特定关系中哪个列承担“外键”角色。

1.3 版本中的新增功能:添加了FunctionElement.as_comparison()。## 重叠的外键

当使用复合外键时,可能会出现罕见的情况,使得单个列可能是通过外键约束引用的多个列的主题。

考虑一个(诚然复杂的)映射,如Magazine对象,由Writer对象和Article对象使用包含magazine_id的复合主键方案引用;然后为了使Article也引用WriterArticle.magazine_id涉及到两个单独的关系;Article.magazineArticle.writer

class Magazine(Base):
    __tablename__ = "magazine"
    id = mapped_column(Integer, primary_key=True)
class Article(Base):
    __tablename__ = "article"
    article_id = mapped_column(Integer)
    magazine_id = mapped_column(ForeignKey("magazine.id"))
    writer_id = mapped_column()
    magazine = relationship("Magazine")
    writer = relationship("Writer")
    __table_args__ = (
        PrimaryKeyConstraint("article_id", "magazine_id"),
        ForeignKeyConstraint(
            ["writer_id", "magazine_id"], ["writer.id", "writer.magazine_id"]
        ),
    )
class Writer(Base):
    __tablename__ = "writer"
    id = mapped_column(Integer, primary_key=True)
    magazine_id = mapped_column(ForeignKey("magazine.id"), primary_key=True)
    magazine = relationship("Magazine")

配置上述映射后,我们将看到发出此警告:

SAWarning: relationship 'Article.writer' will copy column
writer.magazine_id to column article.magazine_id,
which conflicts with relationship(s): 'Article.magazine'
(copies magazine.id to article.magazine_id). Consider applying
viewonly=True to read-only relationships, or provide a primaryjoin
condition marking writable columns with the foreign() annotation.

这指的是Article.magazine_id是两个不同外键约束的主题的事实;它直接引用Magazine.id作为源列,但也在Writer的复合键的上下文中引用Writer.magazine_id作为源列。如果我们将Article与特定的Magazine关联起来,然后将Article与与不同Magazine关联的Writer关联起来,ORM 将非确定性地覆盖Article.magazine_id,在不通知的情况下更改我们引用的杂志;如果我们将WriterArticle中解除关联,它还可能尝试将 NULL 放入此列。警告让我们知道这是这种情况。

要解决这个问题,我们需要将Article的行为分解,包括以下三个特性:

  1. 首先,Article根据仅在Article.magazine关系中持久化的数据写入Article.magazine_id,即从Magazine.id复制的值。
  2. Article可以代表在Article.writer关系中持久化的数据写入Article.writer_id,但仅限于Writer.id列;Writer.magazine_id列不应写入Article.magazine_id,因为它最终源自Magazine.id
  3. 当加载Article.writer时,Article会考虑Article.magazine_id,尽管它在此关系中不会向其写入。

要获得仅#1 和#2,我们可以仅指定Article.writer_id作为Article.writer的“外键”:

class Article(Base):
    # ...
    writer = relationship("Writer", foreign_keys="Article.writer_id")

然而,这会导致Article.writer在与Writer查询时不考虑Article.magazine_id

SELECT  article.article_id  AS  article_article_id,
  article.magazine_id  AS  article_magazine_id,
  article.writer_id  AS  article_writer_id
FROM  article
JOIN  writer  ON  writer.id  =  article.writer_id

因此,为了获得所有#1、#2 和#3,我们通过完全组合relationship.primaryjoinrelationship.foreign_keys参数,或者更简洁地通过用foreign()注释来表达连接条件以及要写入的列:

class Article(Base):
    # ...
    writer = relationship(
        "Writer",
        primaryjoin="and_(Writer.id == foreign(Article.writer_id), "
        "Writer.magazine_id == Article.magazine_id)",
    )

非关系对比/实现路径

警告

此部分详细介绍了一个实验性功能。

使用自定义表达式意味着我们可以生成不遵循通常的主键/外键模型的非正统连接条件。其中一个例子是实现路径模式,其中我们比较字符串以产生重叠路径标记,以便生成树结构。

通过精心使用foreign()remote(),我们可以构建一个有效地产生基本的物化路径系统的关系。基本上,当foreign()remote()相同的比较表达式的一侧时,关系被视为“一对多”;当它们在不同的一侧时,关系被视为“多对一”。对于我们将在此处使用的比较,我们将处理集合,因此保持配置为“一对多”:

class Element(Base):
    __tablename__ = "element"
    path = mapped_column(String, primary_key=True)
    descendants = relationship(
        "Element",
        primaryjoin=remote(foreign(path)).like(path.concat("/%")),
        viewonly=True,
        order_by=path,
    )

上述情况下,如果给定一个具有路径属性为"/foo/bar2"Element对象,我们寻求加载Element.descendants以如下形式:

SELECT  element.path  AS  element_path
FROM  element
WHERE  element.path  LIKE  ('/foo/bar2'  ||  '/%')  ORDER  BY  element.path


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

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
3月前
|
SQL 存储 数据库
SqlAlchemy 2.0 中文文档(十一)(4)
SqlAlchemy 2.0 中文文档(十一)
38 11
|
3月前
|
SQL 数据库 Python
SqlAlchemy 2.0 中文文档(十一)(3)
SqlAlchemy 2.0 中文文档(十一)
36 11
|
3月前
|
存储 SQL Python
SqlAlchemy 2.0 中文文档(十一)(5)
SqlAlchemy 2.0 中文文档(十一)
39 10
|
3月前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(十七)(3)
SqlAlchemy 2.0 中文文档(十七)
34 4
|
3月前
|
SQL 关系型数据库 API
SqlAlchemy 2.0 中文文档(十七)(4)
SqlAlchemy 2.0 中文文档(十七)
53 4
|
3月前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(十七)(2)
SqlAlchemy 2.0 中文文档(十七)
33 4
|
3月前
|
SQL API 数据库
SqlAlchemy 2.0 中文文档(十一)(1)
SqlAlchemy 2.0 中文文档(十一)
37 2
|
3月前
|
SQL 测试技术 数据库
SqlAlchemy 2.0 中文文档(十二)(5)
SqlAlchemy 2.0 中文文档(十二)
21 2
|
3月前
|
存储 SQL 数据库
SqlAlchemy 2.0 中文文档(十一)(2)
SqlAlchemy 2.0 中文文档(十一)
26 2
|
3月前
|
测试技术 Python 容器
SqlAlchemy 2.0 中文文档(十二)(4)
SqlAlchemy 2.0 中文文档(十二)
30 1