SqlAlchemy 2.0 中文文档(十一)(5)

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

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


关系参数的延迟评估

大多数前面部分的示例展示了映射,其中各种relationship()构造使用字符串名称而不是类本身引用其目标类,例如在使用Mapped时,会生成一个仅作为字符串存在的前向引用:

class Parent(Base):
    # ...
    children: Mapped[List["Child"]] = relationship(back_populates="parent")
class Child(Base):
    # ...
    parent: Mapped["Parent"] = relationship(back_populates="children")

同样,在使用非注释形式,如非注释性的声明式或命令式映射时,relationship()构造也直接支持字符串名称:

registry.map_imperatively(
    Parent,
    parent_table,
    properties={"children": relationship("Child", back_populates="parent")},
)
registry.map_imperatively(
    Child,
    child_table,
    properties={"parent": relationship("Parent", back_populates="children")},
)

这些字符串名称在映射器解析阶段被解析为类,这是一个内部过程,通常在定义所有映射之后发生,并且通常由映射本身的第一次使用触发。registry对象是这些名称存储和解析为它们引用的映射类的容器。

除了relationship()的主要类参数之外,还可以指定依赖于尚未定义类上存在的列的其他参数,这些参数可以是 Python 函数,或更常见的是字符串。对于这些参数中的大多数,除了主要参数之外,字符串输入都会使用 Python 内置的 eval()函数评估为 Python 表达式,因为它们旨在接收完整的 SQL 表达式。

警告

由于 Python 的eval()函数用于解释传递给relationship()映射配置构造的延迟评估的字符串参数,这些参数不应该被重新用于接收不受信任的用户输入;eval()对不受信任的用户输入不安全

在这个评估中可用的完整命名空间包括为这个声明基类映射的所有类,以及sqlalchemy包的内容,包括表达式函数如desc()sqlalchemy.sql.functions.func

class Parent(Base):
    # ...
    children: Mapped[List["Child"]] = relationship(
        order_by="desc(Child.email_address)",
        primaryjoin="Parent.id == Child.parent_id",
    )

对于一个模块包含多个同名类的情况,字符串类名也可以在这些字符串表达式中作为模块限定路径指定:

class Parent(Base):
    # ...
    children: Mapped[List["myapp.mymodel.Child"]] = relationship(
        order_by="desc(myapp.mymodel.Child.email_address)",
        primaryjoin="myapp.mymodel.Parent.id == myapp.mymodel.Child.parent_id",
    )

在上述示例中,传递给Mapped的字符串也可以通过直接将类位置字符串传递给relationship.argument来消除特定类参数。下面说明了仅类型导入Child的示例,结合了将运行时说明符与将在registry中搜索正确名称的目标类相结合:

import typing
if typing.TYPE_CHECKING:
    from myapp.mymodel import Child
class Parent(Base):
    # ...
    children: Mapped[List["Child"]] = relationship(
        "myapp.mymodel.Child",
        order_by="desc(myapp.mymodel.Child.email_address)",
        primaryjoin="myapp.mymodel.Parent.id == myapp.mymodel.Child.parent_id",
    )

合格路径可以是任何消除名称之间歧义的部分路径。例如,要消除myapp.model1.Childmyapp.model2.Child之间的歧义,我们可以指定model1.Childmodel2.Child

class Parent(Base):
    # ...
    children: Mapped[List["Child"]] = relationship(
        "model1.Child",
        order_by="desc(mymodel1.Child.email_address)",
        primaryjoin="Parent.id == model1.Child.parent_id",
    )

relationship()构造还接受 Python 函数或 lambda 作为这些参数的输入。Python 函数式方法可能如下所示:

import typing
from sqlalchemy import desc
if typing.TYPE_CHECKING:
    from myapplication import Child
def _resolve_child_model():
    from myapplication import Child
    return Child
class Parent(Base):
    # ...
    children: Mapped[List["Child"]] = relationship(
        _resolve_child_model,
        order_by=lambda: desc(_resolve_child_model().email_address),
        primaryjoin=lambda: Parent.id == _resolve_child_model().parent_id,
    )

完整的参数列表接受 Python 函数/lambda 或将传递给eval()的字符串的参数包括:

  • relationship.order_by
  • relationship.primaryjoin
  • relationship.secondaryjoin
  • relationship.secondary
  • relationship.remote_side
  • relationship.foreign_keys
  • relationship._user_defined_foreign_keys

警告

如前所述,relationship()中的上述参数会作为 Python 代码表达式使用 eval()进行评估。不要将不受信任的输入传递给这些参数。

在声明后将关系添加到映射类

还应注意,与向现有的声明映射类添加附加列中描述的类似方式,任何MapperProperty构造都可以随时添加到声明基础映射中(注意在此上下文中不支持注释形式)。如果我们想要在Address类可用之后实现这个relationship(),我们也可以随后应用它:

# first, module A, where Child has not been created yet,
# we create a Parent class which knows nothing about Child
class Parent(Base): ...
# ... later, in Module B, which is imported after module A:
class Child(Base): ...
from module_a import Parent
# assign the User.addresses relationship as a class variable.  The
# declarative base class will intercept this and map the relationship.
Parent.children = relationship(Child, primaryjoin=Child.parent_id == Parent.id)

与 ORM 映射列一样,Mapped注解类型无法参与此操作;因此,相关类必须直接在relationship()构造中指定,可以是类本身、类的字符串名称或返回目标类引用的可调用函数。

注意

与 ORM 映射列一样,对已映射类的映射属性的赋值仅在使用“声明基类”类时才能正确执行,这意味着用户定义的DeclarativeBase子类或declarative_base()返回的动态生成类或registry.generate_base()。这个“基”类包括一个 Python 元类,实现了一个特殊的__setattr__()方法来拦截这些操作。

如果类使用像registry.mapped()这样的装饰器或像registry.map_imperatively()这样的命令式函数进行映射,则无法在运行时将类映射属性分配给映射类。 ### 使用多对多关系的“secondary”参数的延迟评估形式

多对多关系使用relationship.secondary参数,通常表示对通常不映射的Table对象或其他 Core 可选择对象的引用。通常使用 lambda 可调用进行延迟评估。

对于 Many To Many 中给出的示例,如果我们假设association_table Table对象将在模块中稍后定义,则我们可以使用 lambda 编写relationship()

class Parent(Base):
    __tablename__ = "left_table"
    id: Mapped[int] = mapped_column(primary_key=True)
    children: Mapped[List["Child"]] = relationship(
        "Child", secondary=lambda: association_table
    )

作为也是有效 Python 标识符的表名的快捷方式,relationship.secondary参数也可以作为字符串传递,其中解析通过将字符串作为 Python 表达式进行评估来完成,简单标识符名称链接到当前registry引用的相同命名的Table对象。

在下面的示例中,表达式"association_table"将作为名为"association_table"的变量进行评估,该变量将根据MetaData集合中的表名进行解析:

class Parent(Base):
    __tablename__ = "left_table"
    id: Mapped[int] = mapped_column(primary_key=True)
    children: Mapped[List["Child"]] = relationship(secondary="association_table")

注意

当作为字符串传递时,传递给relationship.secondary的名称必须是有效的 Python 标识符,以字母开头,只包含字母数字字符或下划线。其他字符,如破折号等,将被解释为 Python 运算符,而不会解析为给定的名称。请考虑使用 lambda 表达式而不是字符串以提高清晰度。

警告

当作为字符串传递时,relationship.secondary参数使用 Python 的eval()函数进行解释,即使它通常是一个表的名称。不要将不受信任的输入传递给此字符串。###在声明后向映射类添加关系

还应注意,在类似于 Appending additional columns to an existing Declarative mapped class 描述的方式中,任何MapperProperty构造都可以随时添加到声明基本映射中(注意,此上下文中不支持注释形式)。如果我们希望在Address类可用后实现此relationship(),我们也可以随后应用它:

# first, module A, where Child has not been created yet,
# we create a Parent class which knows nothing about Child
class Parent(Base): ...
# ... later, in Module B, which is imported after module A:
class Child(Base): ...
from module_a import Parent
# assign the User.addresses relationship as a class variable.  The
# declarative base class will intercept this and map the relationship.
Parent.children = relationship(Child, primaryjoin=Child.parent_id == Parent.id)

与 ORM 映射列一样,Mapped注解类型没有参与此操作的能力;因此,相关类必须直接在relationship()构造中指定,可以是类本身、类的字符串名称,或者返回目标类引用的可调用函数。

注意

与 ORM 映射列一样,将映射属性分配给已经映射的类只有在使用“声明式基类”时才能正确运行,这意味着必须使用用户定义的DeclarativeBase子类或者declarative_base()返回的动态生成的类或者registry.generate_base()返回的动态生成的类。这个“基类”包含一个实现了特殊__setattr__()方法的 Python 元类,它拦截这些操作。

如果使用类似于registry.mapped()这样的装饰器或像registry.map_imperatively()这样的命令式函数来映射类,则无法在运行时将映射属性分配给映射类。

使用“secondary”参数的延迟评估形式来处理多对多关系

多对多关系使用relationship.secondary参数,通常表示对通常非映射的Table对象或其他核心可选择对象的引用。典型的延迟评估使用 lambda 可调用。

对于 Many To Many 中给出的例子,如果我们假设association_table Table对象将在模块中的某个后续点被定义,那么我们可以使用 lambda 来编写relationship(),如下所示:

class Parent(Base):
    __tablename__ = "left_table"
    id: Mapped[int] = mapped_column(primary_key=True)
    children: Mapped[List["Child"]] = relationship(
        "Child", secondary=lambda: association_table
    )

作为表名的快捷方式,也可以将relationship.secondary参数传递为字符串,其中解析工作通过将字符串作为 Python 表达式进行评估,简单标识符名称链接到与当前registry引用的相同MetaData集合中存在的同名Table对象。

在下面的示例中,表达式"association_table"被解析为一个名为"association_table"的变量,该变量根据MetaData集合中的表名解析:

class Parent(Base):
    __tablename__ = "left_table"
    id: Mapped[int] = mapped_column(primary_key=True)
    children: Mapped[List["Child"]] = relationship(secondary="association_table")

注意

当作为字符串传递时,传递给relationship.secondary的名称必须是有效的 Python 标识符,以字母开头,仅包含字母数字字符或下划线。其他字符,如破折号等,将被解释为 Python 操作符,而不会解析为给定的名称。请考虑使用 lambda 表达式而不是字符串,以提高清晰度。

警告

当作为字符串传递时,relationship.secondary参数将使用 Python 的eval()函数进行解释,即使它通常是一个表的名称。不要将不受信任的输入传递给该字符串

符串**。###在声明后向映射类添加关系

还应注意,在类似于 Appending additional columns to an existing Declarative mapped class 描述的方式中,任何MapperProperty构造都可以随时添加到声明基本映射中(注意,此上下文中不支持注释形式)。如果我们希望在Address类可用后实现此relationship(),我们也可以随后应用它:

# first, module A, where Child has not been created yet,
# we create a Parent class which knows nothing about Child
class Parent(Base): ...
# ... later, in Module B, which is imported after module A:
class Child(Base): ...
from module_a import Parent
# assign the User.addresses relationship as a class variable.  The
# declarative base class will intercept this and map the relationship.
Parent.children = relationship(Child, primaryjoin=Child.parent_id == Parent.id)

与 ORM 映射列一样,Mapped注解类型没有参与此操作的能力;因此,相关类必须直接在relationship()构造中指定,可以是类本身、类的字符串名称,或者返回目标类引用的可调用函数。

注意

与 ORM 映射列一样,将映射属性分配给已经映射的类只有在使用“声明式基类”时才能正确运行,这意味着必须使用用户定义的DeclarativeBase子类或者declarative_base()返回的动态生成的类或者registry.generate_base()返回的动态生成的类。这个“基类”包含一个实现了特殊__setattr__()方法的 Python 元类,它拦截这些操作。

如果使用类似于registry.mapped()这样的装饰器或像registry.map_imperatively()这样的命令式函数来映射类,则无法在运行时将映射属性分配给映射类。

使用“secondary”参数的延迟评估形式来处理多对多关系

多对多关系使用relationship.secondary参数,通常表示对通常非映射的Table对象或其他核心可选择对象的引用。典型的延迟评估使用 lambda 可调用。

对于 Many To Many 中给出的例子,如果我们假设association_table Table对象将在模块中的某个后续点被定义,那么我们可以使用 lambda 来编写relationship(),如下所示:

class Parent(Base):
    __tablename__ = "left_table"
    id: Mapped[int] = mapped_column(primary_key=True)
    children: Mapped[List["Child"]] = relationship(
        "Child", secondary=lambda: association_table
    )

作为表名的快捷方式,也可以将relationship.secondary参数传递为字符串,其中解析工作通过将字符串作为 Python 表达式进行评估,简单标识符名称链接到与当前registry引用的相同MetaData集合中存在的同名Table对象。

在下面的示例中,表达式"association_table"被解析为一个名为"association_table"的变量,该变量根据MetaData集合中的表名解析:

class Parent(Base):
    __tablename__ = "left_table"
    id: Mapped[int] = mapped_column(primary_key=True)
    children: Mapped[List["Child"]] = relationship(secondary="association_table")

注意

当作为字符串传递时,传递给relationship.secondary的名称必须是有效的 Python 标识符,以字母开头,仅包含字母数字字符或下划线。其他字符,如破折号等,将被解释为 Python 操作符,而不会解析为给定的名称。请考虑使用 lambda 表达式而不是字符串,以提高清晰度。

警告

当作为字符串传递时,relationship.secondary参数将使用 Python 的eval()函数进行解释,即使它通常是一个表的名称。不要将不受信任的输入传递给该字符串

相关文章
|
SQL Oracle 关系型数据库
FastAPI数据库系列(一) MySQL数据库操作 一、简介
FastAPI中你可以使用任何关系型数据库,可以通过SQLAlchemy将其轻松的适应于任何的数据库,比如: PostgreSQL MySQL SQLite Oracle Microsoft SQL Server ...
|
9月前
|
存储 安全 网络安全
如何配置密码策略 - 概述和指南
Active Directory(AD)默认密码策略是确保用户账户安全的重要组成部分,涵盖密码长度、复杂性、历史记录及有效期等关键设置。通过组策略或PowerShell可调整这些策略,以满足企业安全需求。然而,高强度密码策略可能影响用户体验,且面对日益复杂的攻击手段,传统策略已显不足。ADSelfService Plus作为一款企业级AD域密码管理工具,提供密码自助重置、锁定账户解锁、密码黑名单及状态报告等功能,在提升安全性的同时优化用户体验,为企业AD域管理带来高效解决方案。
654 6
|
JavaScript 前端开发 数据库
测试开发之路--Flask 之旅 (四):登录与权限控制
本文介绍了如何使用 Flask-Security 和 Flask-Login 实现权限管理和用户登录功能。首先讲解了 Flask 的消息闪现功能,用于向模板传递信息。接着介绍了如何利用 Flask-Security 的 `roles_required` 和 `roles_accepted` 装饰器保护页面,并展示了如何通过 `current_user` 获取当前用户信息。最后提到了使用 Flask-Login 时应避免升级至 0.4.0 版本,以防出现兼容性问题。通过这些技术,搭建了一个基本的用户权限管理系统。
634 6
测试开发之路--Flask 之旅 (四):登录与权限控制
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(七十二)(2)
SqlAlchemy 2.0 中文文档(七十二)
281 0
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(七十二)(3)
SqlAlchemy 2.0 中文文档(七十二)
481 0
|
数据采集 机器学习/深度学习 自然语言处理
一文讲懂大模型调优技术
随着AI技术的发展,大模型如GPT系列、BERT等成为推动自然语言处理和计算机视觉领域进步的重要驱动力。然而,大模型的调优过程复杂且资源消耗巨大,对开发者构成严峻挑战。本文旨在全面解析大模型调优的关键技术,涵盖数据预处理、模型架构调整、超参数优化、正则化与泛化能力提升,以及分布式训练与并行优化等内容,为开发者提供系统性的调优指南。
|
SQL 测试技术 Python
SqlAlchemy 2.0 中文文档(四)(4)
SqlAlchemy 2.0 中文文档(四)
270 3
|
SQL 存储 数据库连接
SqlAlchemy 2.0 中文文档(二十二)(1)
SqlAlchemy 2.0 中文文档(二十二)
284 2
|
缓存 搜索推荐 数据库连接
FastAPI 的插件化也太牛了吧!轻松打造可扩展 Web 应用,让你的开发如虎添翼,快来感受这神奇魅力!
【8月更文挑战第31天】在互联网迅速发展的今天,Web应用需求日益多样化。FastAPI作为一种高效灵活的Python Web框架,通过插件化设计帮助开发者轻松构建可扩展应用。插件化让开发者能够根据需求选择合适模块,如用户认证、支付处理等,而无需大幅改动核心代码。这种方式不仅提高了开发效率,还增强了应用的稳定性和适应性,使FastAPI成为未来Web开发的理想平台。
584 0

热门文章

最新文章