SqlAlchemy 2.0 中文文档(二十九)(3)

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
简介: SqlAlchemy 2.0 中文文档(二十九)

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


参数:

  • other – 要比较的表达式。通常这是一个普通字符串值,但也可以是任意的 SQL 表达式。LIKE 通配符字符%_默认情况下不会被转义,除非设置了ColumnOperators.startswith.autoescape 标志为 True。
  • autoescape
    boolean;当为 True 时,在 LIKE 表达式中建立一个转义字符,然后将其应用于比较值中的所有出现的"%""_"和转义字符本身,假定比较值是一个字面字符串而不是 SQL 表达式。
    例如:
somecolumn.startswith("foo%bar", autoescape=True)
  • 将渲染为:
somecolumn LIKE :param || '%' ESCAPE '/'
  • 具有param的值为"foo/%bar"
  • escape
    当给定的字符与ESCAPE关键字一起使用时,将渲染为逃逸字符。然后,可以将该字符放置在%_的前面,以允许它们作为自身而不是通配符字符。
    例如:
somecolumn.startswith("foo/%bar", escape="^")
  • 将渲染为:
somecolumn LIKE :param || '%' ESCAPE '^'
  • 参数还可以与 ColumnOperators.startswith.autoescape 结合使用:
somecolumn.startswith("foo%bar^bat", escape="^", autoescape=True)
  • 在上面的例子中,给定的字面参数将在传递到数据库之前转换为"foo^%bar^^bat"

另请参阅

ColumnOperators.endswith()

ColumnOperators.contains()

ColumnOperators.like()

attribute target_class: Type[Any]

AssociationProxyInstance 处理的中介类。

拦截的追加/设置/赋值事件将导致生成此类的新实例。

attribute timetuple: Literal[None] = None

继承自 ColumnOperators.timetuple 属性的 ColumnOperators

黑客,允许在左手边比较日期时间对象。

class sqlalchemy.ext.associationproxy.ColumnAssociationProxyInstance

一个将数据库列作为目标的 AssociationProxyInstance

成员

le(), lt(), ne(),  all_(), any(), any_(), asc(), attr, between(), bitwise_and(),  bitwise_lshift(), bitwise_not(), bitwise_or(), bitwise_rshift(),  bitwise_xor(), bool_op(), collate(), concat(), contains(), desc(),  distinct(), endswith(), has(), icontains(), iendswith(), ilike(), in_(),  is_(), is_distinct_from(), is_not(), is_not_distinct_from(), isnot(),  isnot_distinct_from(), istartswith(), like(), local_attr, match(),  not_ilike(), not_in(), not_like(), notilike(), notin_(), notlike(),  nulls_first(), nulls_last(), nullsfirst(), nullslast(), op(), operate(),  regexp_match(), regexp_replace(), remote_attr, reverse_operate(),  scalar, startswith(), target_class, timetuple

类签名

sqlalchemy.ext.associationproxy.ColumnAssociationProxyInstancesqlalchemy.ext.associationproxy.AssociationProxyInstance)。

method __le__(other: Any) → ColumnOperators

继承自 sqlalchemy.sql.expression.ColumnOperators.__le__ 方法的 ColumnOperators

实现 <= 运算符。

在列的上下文中,产生子句 a <= b

method __lt__(other: Any) → ColumnOperators

继承自 sqlalchemy.sql.expression.ColumnOperators.__lt__ 方法的 ColumnOperators

实现 < 运算符。

在列的上下文中,产生子句 a < b

method __ne__(other: Any) → ColumnOperators

继承自 sqlalchemy.sql.expression.ColumnOperators.__ne__ 方法的 ColumnOperators

实现 != 运算符。

在列的上下文中,产生子句 a != b。如果目标是 None,则产生 a IS NOT NULL

method all_() → ColumnOperators

继承自 ColumnOperators.all_() 方法的 ColumnOperators

对父对象生成一个all_() 子句。

参见all_() 的文档以获取示例。

注意

一定要不要混淆新版本的 ColumnOperators.all_() 方法与 遗留 版本的该方法,即 Comparator.all() 方法,该方法特定于 ARRAY,使用不同的调用方式。

method any(criterion: _ColumnExpressionArgument[bool] | None = None, **kwargs: Any) → ColumnElement[bool]

继承自 AssociationProxyInstance.any() 方法的 AssociationProxyInstance

使用 EXISTS 生成一个代理的‘any’表达式。

此表达式将使用底层代理属性的 Comparator.any() 和/或 Comparator.has() 运算符进行组合。

method any_() → ColumnOperators

ColumnOperators ColumnOperators.any_() 方法继承

生成一个针对父对象的 any_() 子句。

请参考 any_() 的文档以获取示例。

注意

请确保不要混淆较新的 ColumnOperators.any_() 方法与此方法的传统版本,即 ARRAY 特定的 Comparator.any() 方法,后者使用不同的调用风格。

method asc() → ColumnOperators

ColumnOperators ColumnOperators.asc() 方法继承

产生一个针对父对象的 asc() 子句。

attribute attr

AssociationProxyInstance AssociationProxyInstance.attr 属性继承

返回一个 (local_attr, remote_attr) 元组。

该属性最初旨在简化使用 Query.join() 方法一次跨两个关系进行联接,但这样做使用了一个已弃用的调用风格。

要使用 select.join()Query.join() 与关联代理,当前的方法是分别使用 AssociationProxyInstance.local_attrAssociationProxyInstance.remote_attr 属性:

stmt = (
    select(Parent).
    join(Parent.proxied.local_attr).
    join(Parent.proxied.remote_attr)
)

未来的版本可能会提供更简洁的关联代理属性连接模式。

另请参阅

AssociationProxyInstance.local_attr

AssociationProxyInstance.remote_attr

method between(cleft: Any, cright: Any, symmetric: bool = False) → ColumnOperators

继承自 ColumnOperators.between() 方法的 ColumnOperators

对父对象生成between()子句,给定下限和上限范围。

method bitwise_and(other: Any) → ColumnOperators

继承自 ColumnOperators.bitwise_and() 方法的 ColumnOperators

通过&操作符进行位与操作。

2.0.2 版本中新增。

另请参阅

位运算符

method bitwise_lshift(other: Any) → ColumnOperators

继承自 ColumnOperators.bitwise_lshift() 方法的 ColumnOperators

通过<<操作符进行位左移操作。

2.0.2 版本中新增。

另请参阅

位运算符

method bitwise_not() → ColumnOperators

继承自 ColumnOperators.bitwise_not() 方法的 ColumnOperators

通过~操作符进行位取反操作。

2.0.2 版本中新增。

另请参阅

位运算符

method bitwise_or(other: Any) → ColumnOperators

继承自 ColumnOperators.bitwise_or() 方法的 ColumnOperators

通过|操作符进行位或操作。

2.0.2 版本中新增。

另请参阅

位运算符

method bitwise_rshift(other: Any) → ColumnOperators

继承自 ColumnOperators.bitwise_rshift() 方法的 ColumnOperators

生成一个按位右移操作,通常通过 >> 运算符实现。

新版本 2.0.2 中新增。

另请参阅

按位运算符

method bitwise_xor(other: Any) → ColumnOperators

继承自 ColumnOperators.bitwise_xor() 方法的 ColumnOperators

生成一个按位异或操作,通常通过 ^ 运算符实现,或者对于 PostgreSQL 使用 #

新版本 2.0.2 中新增。

另请参阅

按位运算符

method bool_op(opstring: str, precedence: int = 0, python_impl: Callable[[...], Any] | None = None) → Callable[[Any], Operators]

继承自 Operators.bool_op() 方法的 Operators

返回一个自定义布尔运算符。

这个方法是调用 Operators.op() 并传递 Operators.op.is_comparison 标志为 True 的简写。 使用 Operators.bool_op() 的一个关键优势是,在使用列构造时,返回表达式的“布尔”性质将出现在 PEP 484 目的中。

另请参阅

Operators.op()

method collate(collation: str) → ColumnOperators

继承自 ColumnOperators.collate() 方法的 ColumnOperators

生成一个针对父对象的 collate() 子句,给定排序规则字符串。

另请参阅

collate()

method concat(other: Any) → ColumnOperators

继承自 ColumnOperators.concat() 方法 of ColumnOperators

实现 ‘concat’ 运算符。

在列上下文中,生成子句 a || b,或者在 MySQL 上使用 concat() 运算符。

method contains(other: Any, **kw: Any) → ColumnOperators

继承自 ColumnOperators.contains() 方法 of ColumnOperators

实现 ‘contains’ 运算符。

生成一个 LIKE 表达式,用于测试字符串值中间的匹配:

column LIKE '%' || <other> || '%'

例如:

stmt = select(sometable).\
    where(sometable.c.column.contains("foobar"))

由于运算符使用了 LIKE,所以在 表达式中存在的通配符字符 "%""_" 也将像通配符一样起作用。 对于字面字符串值,可以将 ColumnOperators.contains.autoescape 标志设置为 True,以对字符串值中这些字符的出现应用转义,使它们与自身匹配而不是通配符字符。 或者,ColumnOperators.contains.escape 参数将建立一个给定字符作为转义字符,当目标表达式不是字面字符串时,这可能会有所帮助。

参数:

  • other – 要比较的表达式。 这通常是一个普通字符串值,但也可以是任意的 SQL 表达式。 除非设置了 ColumnOperators.contains.autoescape 标志为 True,否则 LIKE 通配符字符 %_ 不会被转义。
  • autoescape
    布尔值;当为 True 时,在 LIKE 表达式中建立转义字符,然后将其应用于比较值中所有出现的 "%""_" 和转义字符本身,比较值假定为字面字符串而不是 SQL 表达式。
    诸如:
somecolumn.contains("foo%bar", autoescape=True)
  • 将呈现为:
somecolumn LIKE '%' || :param || '%' ESCAPE '/'
  • 值为 :param 的情况下,为 "foo/%bar"
  • escape
    给定字符,当指定时,将使用 ESCAPE 关键字来将该字符建立为转义字符。 然后,可以将此字符放置在 %_ 的出现之前,以允许它们作为自身而不是通配符字符。
    诸如:
somecolumn.contains("foo/%bar", escape="^")
  • 将呈现为:
somecolumn LIKE '%' || :param || '%' ESCAPE '^'
  • 参数也可以与ColumnOperators.contains.autoescape结合使用:
somecolumn.contains("foo%bar^bat", escape="^", autoescape=True)
  • 在上述情况中,给定的文字参数将在传递到数据库之前转换为"foo^%bar^^bat"

参见

ColumnOperators.startswith()

ColumnOperators.endswith()

ColumnOperators.like()

method desc() → ColumnOperators

继承自 ColumnOperators.desc() 方法的 ColumnOperators

对父对象生成一个desc()子句。

method distinct() → ColumnOperators

继承自 ColumnOperators.distinct() 方法的 ColumnOperators

对父对象生成一个distinct()子句。

method endswith(other: Any, escape: str | None = None, autoescape: bool = False) → ColumnOperators

继承自 ColumnOperators.endswith() 方法的 ColumnOperators

实现‘endswith’运算符。

产生一个 LIKE 表达式,测试字符串值的结尾是否匹配:

column LIKE '%' || <other>

例如:

stmt = select(sometable).\
    where(sometable.c.column.endswith("foobar"))

由于该操作符使用LIKE,在表达式中存在的通配符字符"%""_"也将像通配符一样起作用。对于字面字符串值,可以将ColumnOperators.endswith.autoescape标志设置为True,以对字符串值中这些字符的出现应用转义,使它们匹配为自身而不是通配符字符。或者,ColumnOperators.endswith.escape参数将建立一个给定字符作为转义字符,当目标表达式不是字面字符串时,这可能会有用。

参数:

  • other – 要进行比较的表达式。通常是一个普通的字符串值,但也可以是任意的 SQL 表达式。除非将ColumnOperators.endswith.autoescape标志设置为 True,否则不会转义 LIKE 通配符%_
  • autoescape
    布尔值;当为 True 时,在 LIKE 表达式中建立一个转义字符,然后将其应用于比较值中所有的"%""_"和转义字符本身的出现,假定比较值是一个字面字符串而不是 SQL 表达式。
    例如:
somecolumn.endswith("foo%bar", autoescape=True)
  • 将呈现为:
somecolumn LIKE '%' || :param ESCAPE '/'
  • 值为:param,为"foo/%bar"
  • escape
    一个字符,当给定时,将使用ESCAPE关键字将该字符作为转义字符。然后可以将此字符放在%_之前,以允许它们作为自身而不是通配符字符。
    例如:
somecolumn.endswith("foo/%bar", escape="^")
  • 将呈现为:
somecolumn LIKE '%' || :param ESCAPE '^'
  • 该参数也可以与ColumnOperators.endswith.autoescape结合使用:
somecolumn.endswith("foo%bar^bat", escape="^", autoescape=True)
  • 在上述情况下,给定的字面参数将在传递到数据库之前转换为"foo^%bar^^bat"

另请参阅

ColumnOperators.startswith()

ColumnOperators.contains()

ColumnOperators.like()

method has(criterion: _ColumnExpressionArgument[bool] | None = None, **kwargs: Any) → ColumnElement[bool]

继承自 AssociationProxyInstance.has() 方法的 AssociationProxyInstance

使用 EXISTS 生成一个代理的‘has’表达式。

此表达式将使用基础代理属性的Comparator.any()和/或Comparator.has()运算符组成的产品。

method icontains(other: Any, **kw: Any) → ColumnOperators

继承自 ColumnOperators.icontains() 方法的 ColumnOperators

实现icontains运算符,例如ColumnOperators.contains()的不区分大小写版本。

生成一个 LIKE 表达式,用于对字符串值中间的不区分大小写匹配进行测试:

lower(column) LIKE '%' || lower(<other>) || '%'

例如:

stmt = select(sometable).\
    where(sometable.c.column.icontains("foobar"))

由于该运算符使用LIKE,因此存在于表达式中的通配符"%""_"也将像通配符一样起作用。对于字面字符串值,可以将ColumnOperators.icontains.autoescape标志设置为True,以对字符串值中这些字符的出现进行转义,使它们作为自身而不是通配符字符进行匹配。或者,ColumnOperators.icontains.escape参数将建立一个给定字符作为转义字符,当目标表达式不是字面字符串时,这可能会有用。

参数:

  • other – 要进行比较的表达式。通常是一个普通字符串值,但也可以是任意 SQL 表达式。除非将ColumnOperators.icontains.autoescape标志设置为 True,否则不会转义 LIKE 通配符%_
  • autoescape
    布尔值;当为 True 时,在 LIKE 表达式中建立一个转义字符,然后将其应用于比较值中所有出现的"%""_"和转义字符本身,假定比较值是一个字面字符串而不是 SQL 表达式。
    例如这样的表达式:
somecolumn.icontains("foo%bar", autoescape=True)
  • 将呈现为:
lower(somecolumn) LIKE '%' || lower(:param) || '%' ESCAPE '/'
  • 使用值:param"foo/%bar"
  • escape
    一个字符,当给定时,将与ESCAPE关键字一起呈现,将该字符作为转义字符。然后可以将该字符放置在%_的前面,以使它们像自己一样工作,而不是通配符字符。
    例如以下表达式:
somecolumn.icontains("foo/%bar", escape="^")
  • 将呈现为:
lower(somecolumn) LIKE '%' || lower(:param) || '%' ESCAPE '^'
  • 此参数也可以与ColumnOperators.contains.autoescape组合使用:
somecolumn.icontains("foo%bar^bat", escape="^", autoescape=True)
  • 在上述示例中,给定的文字参数在传递到数据库之前将被转换为"foo^%bar^^bat"

另请参阅

ColumnOperators.contains()

method iendswith(other: Any, escape: str | None = None, autoescape: bool = False) → ColumnOperators

继承自 ColumnOperators.iendswith() 方法的 ColumnOperators

实现iendswith操作符,例如,ColumnOperators.endswith()的不区分大小写版本。

生成一个 LIKE 表达式,用于对字符串值的末尾进行不区分大小写的匹配:

lower(column) LIKE '%' || lower(<other>)

例如:

stmt = select(sometable).\
    where(sometable.c.column.iendswith("foobar"))

由于操作符使用LIKE,因此存在于表达式中的通配符字符"%""_"也将像通配符一样工作。对于字面字符串值,可以将ColumnOperators.iendswith.autoescape标志设置为True,以对字符串值内的这些字符进行转义,使它们匹配为自身而不是通配符字符。或者,ColumnOperators.iendswith.escape参数将确定给定字符作为转义字符,当目标表达式不是字面字符串时,这可能会有用。

参数:

  • other – 要进行比较的表达式。这通常是一个普通的字符串值,但也可以是任意的 SQL 表达式。LIKE 通配符字符%_默认情况下不会被转义,除非设置了ColumnOperators.iendswith.autoescape标志为 True。
  • autoescape
    布尔值;当为 True 时,在 LIKE 表达式中建立转义字符,然后将其应用于比较值中的所有出现的"%""_"和转义字符本身,假定比较值是一个字面字符串而不是一个 SQL 表达式。
    诸如:
somecolumn.iendswith("foo%bar", autoescape=True)
  • 将呈现为:
lower(somecolumn) LIKE '%' || lower(:param) ESCAPE '/'
  • 具有值:param"foo/%bar"
  • escape
    一个字符,当给定时,将呈现为带有ESCAPE关键字以将该字符作为转义字符。然后可以将此字符放在%_的前面,以允许它们充当它们自己而不是通配符字符。
    诸如:
somecolumn.iendswith("foo/%bar", escape="^")
  • 将呈现为:
lower(somecolumn) LIKE '%' || lower(:param) ESCAPE '^'
  • 该参数还可以与ColumnOperators.iendswith.autoescape组合:
somecolumn.endswith("foo%bar^bat", escape="^", autoescape=True)
  • 在上述情况下,给定的字面参数将在传递到数据库之前转换为"foo^%bar^^bat"

另请参阅

ColumnOperators.endswith()

method ilike(other: Any, escape: str | None = None) → ColumnOperators

继承自 ColumnOperators.ilike() 的方法 ColumnOperators

实现ilike运算符,例如,不区分大小写的 LIKE。

在列上下文中,产生一个形式为:

lower(a) LIKE lower(other)

或在支持 ILIKE 运算符的后端上:

a ILIKE other

例如:

stmt = select(sometable).\
    where(sometable.c.column.ilike("%foobar%"))

参数:

  • other – 要比较的表达式
  • escape
    可选的转义字符,呈现ESCAPE关键字,例如:
somecolumn.ilike("foo/%bar", escape="/")

另请参阅

ColumnOperators.like()

method in_(other: Any) → ColumnOperators

继承自 ColumnOperators.in_() 的方法 ColumnOperators

实现in运算符。

在列上下文中,生成子句column IN

给定参数other可能是:

  • 字面值列表,例如:
stmt.where(column.in_([1, 2, 3]))
  • 在这种调用形式中,项目列表被转换为与给定列表长度相同的一组绑定参数:
WHERE COL IN (?, ?, ?)
  • 如果比较是针对包含多个表达式的tuple_(),则可以提供元组列表:
from sqlalchemy import tuple_
stmt.where(tuple_(col1, col2).in_([(1, 10), (2, 20), (3, 30)]))
  • 空列表,例如:
stmt.where(column.in_([]))
  • 在这种调用形式中,该表达式呈现一个“空集”表达式。这些表达式针对各个后端进行了定制,并且通常试图将空的 SELECT 语句作为子查询。例如,在 SQLite 上,该表达式是:
WHERE col IN (SELECT 1 FROM (SELECT 1) WHERE 1!=1)
  • 自版本 1.4 更改:在所有情况下,空的 IN 表达式现在使用执行时生成的 SELECT 子查询。
  • 可以使用绑定参数,例如bindparam(),如果包含bindparam.expanding标志:
stmt.where(column.in_(bindparam('value', expanding=True)))
  • 在这种调用形式中,该表达式呈现一个特殊的非 SQL 占位符表达式,看起来像:
WHERE COL IN ([EXPANDING_value])
  • 此占位符表达式在语句执行时被拦截,以转换为前面所示的可变数量的绑定参数形式。如果执行语句如下:
connection.execute(stmt, {"value": [1, 2, 3]})
  • 数据库将为每个值传递一个绑定参数:
WHERE COL IN (?, ?, ?)
  • 自版本 1.2 新增:添加了“扩展”绑定参数
    如果传递一个空列表,则会呈现一个特殊的“空列表”表达式,该表达式特定于正在使用的数据库。在 SQLite 上,这将是:
WHERE COL IN (SELECT 1 FROM (SELECT 1) WHERE 1!=1)
  • 自版本 1.3 新增:现在支持空列表的“扩展”绑定参数
  • 一个select()构造,通常是一个相关的标量选择:
stmt.where(
    column.in_(
        select(othertable.c.y).
        where(table.c.x == othertable.c.x)
    )
)
  • 在这种调用形式中,ColumnOperators.in_()呈现如下:
WHERE COL IN (SELECT othertable.y
FROM othertable WHERE othertable.x = table.x)

参数:

other – 一个字面量列表,一个select()构造,或者一个包含设置为 True 的bindparam()构造,其中包括bindparam.expanding标志。

method is_(other: Any) → ColumnOperators

继承自 ColumnOperators.is_() 方法的 ColumnOperators

实现IS运算符。

通常,当与None的值进行比较时,会自动生成IS,这会解析为NULL。然而,在某些平台上,如果与布尔值进行比较,则可能希望显式使用IS

另请参阅

ColumnOperators.is_not()

method is_distinct_from(other: Any) → ColumnOperators

继承自 ColumnOperators.is_distinct_from() 方法的 ColumnOperators

实现 IS DISTINCT FROM 操作符。

在大多数平台上渲染为 “a IS DISTINCT FROM b”;在某些平台上(如 SQLite)可能渲染为 “a IS NOT b”。

method is_not(other: Any) → ColumnOperators

继承自 ColumnOperators.is_not() 方法的 ColumnOperators

实现 IS NOT 操作符。

通常,与 None 值比较时会自动生成 IS NOT,它解析为 NULL。然而,在某些平台上,如果与布尔值比较,显式使用 IS NOT 可能更可取。

从版本 1.4 起更改:is_not() 操作符从先前版本的 isnot() 重命名。先前的名称保留以保持向后兼容性。

另请参阅

ColumnOperators.is_()

method is_not_distinct_from(other: Any) → ColumnOperators

继承自 ColumnOperators.is_not_distinct_from() 方法的 ColumnOperators

实现 IS NOT DISTINCT FROM 操作符。

在大多数平台上渲染为 “a IS NOT DISTINCT FROM b”;在某些平台上(如 SQLite)可能渲染为 “a IS b”。

从版本 1.4 起更改:is_not_distinct_from() 操作符从先前版本的 isnot_distinct_from() 重命名。先前的名称保留以保持向后兼容性。

method isnot(other: Any) → ColumnOperators

继承自 ColumnOperators.isnot() 方法的 ColumnOperators

实现 IS NOT 操作符。

通常,与 None 值比较时会自动生成 IS NOT,它解析为 NULL。然而,在某些平台上,如果与布尔值比较,显式使用 IS NOT 可能更可取。

从版本 1.4 起更改:is_not() 操作符从先前版本的 isnot() 重命名。先前的名称保留以保持向后兼容性。

另请参阅

ColumnOperators.is_()

method isnot_distinct_from(other: Any) → ColumnOperators

继承自ColumnOperators.isnot_distinct_from()方法的 ColumnOperators

实现IS NOT DISTINCT FROM运算符。

在大多数平台上呈现“a IS NOT DISTINCT FROM b”;在某些平台上,如 SQLite,可能呈现“a IS b”。

在 1.4 版中更改:is_not_distinct_from()运算符从先前版本的isnot_distinct_from()重新命名。先前的名称仍然可用以实现向后兼容性。

method istartswith(other: Any, escape: str | None = None, autoescape: bool = False) → ColumnOperators

继承自ColumnOperators.istartswith()方法的 ColumnOperators

实现istartswith运算符,例如ColumnOperators.startswith()的不区分大小写版本。

生成一个 LIKE 表达式,用于对字符串值的开头进行不区分大小写的匹配:

lower(column) LIKE lower(<other>) || '%'

例如:

stmt = select(sometable).\
    where(sometable.c.column.istartswith("foobar"))

由于该运算符使用了LIKE,因此在表达式内部存在的通配符"%""_"也会像通配符一样起作用。对于文本字符串值,可以将ColumnOperators.istartswith.autoescape标志设置为True,以对字符串值内出现的这些字符进行转义,以便它们匹配为其自身而不是通配符。另外,ColumnOperators.istartswith.escape参数将建立一个给定字符作为转义字符,当目标表达式不是文本字符串时可能会有用。

参数:

  • other – 要比较的表达式。通常这是一个普通的字符串值,但也可以是一个任意的 SQL 表达式。除非设置了ColumnOperators.istartswith.autoescape标志为 True,否则%_这两个 LIKE 通配符默认不会被转义。
  • autoescape
    布尔值;当为 True 时,在 LIKE 表达式内建立转义字符,然后将其应用于比较值中的所有出现的"%""_"以及转义字符本身,假定比较值是一个文本字符串而不是一个 SQL 表达式。
    一个表达式,比如:
somecolumn.istartswith("foo%bar", autoescape=True)
  • 渲染为:
lower(somecolumn) LIKE lower(:param) || '%' ESCAPE '/'
  • 参数值为 :param 时为 "foo/%bar"
  • escape
    一个字符,当给定时将与 ESCAPE 关键字一起渲染,将该字符作为转义字符。然后可以将此字符放在 %_ 的前面,以使它们作为自身而不是通配符字符。
    一个表达式,比如:
somecolumn.istartswith("foo/%bar", escape="^")
  • 渲染为:
lower(somecolumn) LIKE lower(:param) || '%' ESCAPE '^'
  • 此参数也可以与 ColumnOperators.istartswith.autoescape 结合使用:
somecolumn.istartswith("foo%bar^bat", escape="^", autoescape=True)
  • 在上面的例子中,给定的文字参数在传递到数据库之前会被转换为"foo^%bar^^bat"

另见

ColumnOperators.startswith()

method like(other: Any, escape: str | None = None) → ColumnOperators

继承自 ColumnOperators.like() 方法的 ColumnOperators

实现 like 运算符。

在列上下文中,产生如下表达式:

a LIKE other

例如:

stmt = select(sometable).\
    where(sometable.c.column.like("%foobar%"))

参数:

  • other – 待比较的表达式
  • escape
    可选的转义字符,渲染为 ESCAPE 关键字,例如:
somecolumn.like("foo/%bar", escape="/")

另见

ColumnOperators.ilike()

attribute local_attr

继承自 AssociationProxyInstance.local_attr 属性的 AssociationProxyInstance

由此 AssociationProxyInstance 引用的 ‘local’ 类属性。

另见

AssociationProxyInstance.attr

AssociationProxyInstance.remote_attr

method match(other: Any, **kwargs: Any) → ColumnOperators

继承自 ColumnOperators.match() 方法的 ColumnOperators

实现了数据库特定的 ‘match’ 运算符。

ColumnOperators.match() 尝试解析为后端提供的 MATCH-like 函数或运算符。例如:

  • PostgreSQL - 渲染 x @@ plainto_tsquery(y)

从版本 2.0 开始:对于 PostgreSQL,现在使用 plainto_tsquery() 而不是 to_tsquery();有关与其他形式的兼容性,请参阅 全文搜索。

  • MySQL - 渲染 MATCH (x) AGAINST (y IN BOOLEAN MODE)
    另请参阅
    match - 具有附加功能的特定于 MySQL 的构造。
  • Oracle - 渲染 CONTAINS(x, y)
  • 其他后端可能提供特殊的实现。
  • 没有特殊实现的后端将将运算符发出为“MATCH”。例如,这与 SQLite 兼容。
method not_ilike(other: Any, escape: str | None = None) → ColumnOperators

继承自 ColumnOperators.not_ilike() 方法 ColumnOperators

实现 NOT ILIKE 运算符。

这相当于使用 ColumnOperators.ilike() 的否定,即 ~x.ilike(y)

从版本 1.4 开始:not_ilike() 运算符从先前版本的 notilike() 重命名。先前的名称仍可用于向后兼容。

另请参阅

ColumnOperators.ilike()

method not_in(other: Any) → ColumnOperators

继承自 ColumnOperators.not_in() 方法 ColumnOperators

实现 NOT IN 运算符。

这相当于使用 ColumnOperators.in_() 的否定,即 ~x.in_(y)

如果 other 是一个空序列,编译器将生成一个“空不在”表达式。默认情况下,这会产生一个“1 = 1”的表达式,在所有情况下都返回 true。可以使用 create_engine.empty_in_strategy 来更改此行为。

从版本 1.4 开始:not_in() 运算符从先前版本的 notin_() 重命名。先前的名称仍可用于向后兼容。

从版本 1.2 开始更改:ColumnOperators.in_()ColumnOperators.not_in() 运算符现在默认情况下为一个空的 IN 序列生成“静态”表达式。

另请参阅

ColumnOperators.in_()

method not_like(other: Any, escape: str | None = None) → ColumnOperators

继承自 ColumnOperators.not_like() 方法的 ColumnOperators

实现NOT LIKE运算符。

这等同于对ColumnOperators.like()使用否定,即~x.like(y)

从版本 1.4 开始更改:not_like()运算符从先前版本的notlike()重命名。 以确保向后兼容性,先前的名称仍然可用。

另请参阅

ColumnOperators.like()

method notilike(other: Any, escape: str | None = None) → ColumnOperators

继承自 ColumnOperators.notilike() 方法的 ColumnOperators

实现NOT ILIKE运算符。

这等同于对ColumnOperators.ilike()使用否定,即~x.ilike(y)

从版本 1.4 开始更改:not_ilike()运算符从先前版本的notilike()重命名。 以确保向后兼容性,先前的名称仍然可用。

另请参阅

ColumnOperators.ilike()

method notin_(other: Any) → ColumnOperators

继承自 ColumnOperators.notin_() 方法的 ColumnOperators

实现NOT IN运算符。

这等同于对ColumnOperators.in_()使用否定,即~x.in_(y)

如果 other 是一个空序列,则编译器会生成一个“空的不在”表达式。 默认情况下,这会默认为表达式“1 = 1”,以在所有情况下产生 true。 可以使用 create_engine.empty_in_strategy 来更改此行为。

自版本 1.4 起:not_in() 操作符在之前的版本中从 notin_() 重命名。 以前的名称仍然可用于向后兼容。

自版本 1.2 起:ColumnOperators.in_()ColumnOperators.not_in() 操作符现在默认生成一个空的 IN 序列的“静态”表达式。

另请参阅

ColumnOperators.in_()

method notlike(other: Any, escape: str | None = None) → ColumnOperators

继承自 ColumnOperators.notlike() 方法的 ColumnOperators

实现 NOT LIKE 操作符。

这相当于使用 ColumnOperators.like() 的否定,即 ~x.like(y)

自版本 1.4 起:not_like() 操作符在之前的版本中从 notlike() 重命名。 以前的名称仍然可用于向后兼容。

另请参阅

ColumnOperators.like()

method nulls_first() → ColumnOperators

继承自 ColumnOperators.nulls_first() 方法的 ColumnOperators

产生一个针对父对象的 nulls_first() 子句。

自版本 1.4 起:nulls_first() 操作符在之前的版本中从 nullsfirst() 重命名。 以前的名称仍然可用于向后兼容。

method nulls_last() → ColumnOperators

继承自 ColumnOperators.nulls_last() 方法的 ColumnOperators

产生一个针对父对象的 nulls_last() 子句。

在版本 1.4 中更改:nulls_last() 运算符从之前的版本中的 nullslast() 重新命名。以前的名称仍然可用于向后兼容。

method nullsfirst() → ColumnOperators

ColumnOperators ColumnOperators.nullsfirst() 方法继承

产生一个针对父对象的 nulls_first() 子句。

在版本 1.4 中更改:nulls_first() 运算符从之前的版本中的 nullsfirst() 重新命名。以前的名称仍然可用于向后兼容。

method nullslast() → ColumnOperators

ColumnOperators ColumnOperators.nullslast() 方法继承

产生一个针对父对象的 nulls_last() 子句。

在版本 1.4 中更改:nulls_last() 运算符从之前的版本中的 nullslast() 重新命名。以前的名称仍然可用于向后兼容。

method op(opstring: str, precedence: int = 0, is_comparison: bool = False, return_type: Type[TypeEngine[Any]] | TypeEngine[Any] | None = None, python_impl: Callable[..., Any] | None = None) → Callable[[Any], Operators]

Operators Operators.op() 方法继承

生成一个通用的运算符函数。

例如:

somecolumn.op("*")(5)

产生:

somecolumn * 5

这个函数也可以用来使按位运算符明确。例如:

somecolumn.op('&')(0xff)

是值在 somecolumn 中的按位与。

参数:

  • opstring – 一个字符串,它将作为中缀运算符输出,在这个元素和传递给生成函数的表达式之间。
  • precedence
    数据库预期应用于 SQL 表达式中的运算符的优先级。这个整数值充当 SQL 编译器的提示,以便知道何时应该在特定操作周围呈现显式的括号。较低的数字会导致表达式在应用于具有更高优先级的另一个运算符时被括起来。默认值 0 低于所有运算符,除了逗号 (,) 和 AS 运算符。值为 100 将高于或等于所有运算符,而 -100 将低于或等于所有运算符。
    另请参阅
    我正在使用 op()生成自定义操作符,但我的括号没有正确显示 - SQLAlchemy SQL 编译器如何渲染括号的详细描述
  • is_comparison
    legacy;如果为 True,则将该操作符视为“比较”操作符,即评估为布尔真/假值的操作符,如==>等。提供此标志是为了使 ORM 关系能够在自定义连接条件中使用操作符时建立该操作符为比较操作符。
    使用is_comparison参数已被Operators.bool_op()方法取代;这个更简洁的操作符会自动设置此参数,同时提供正确的PEP 484类型支持,因为返回的对象将表示“布尔”数据类型,即BinaryExpression[bool]
  • return_type – 一个TypeEngine类或对象,将强制此操作符生成的表达式的返回类型为该类型。默认情况下,指定Operators.op.is_comparison的操作符将解析为Boolean,而未指定的操作符将与左操作数的类型相同。
  • python_impl
    一个可选的  Python 函数,可以在数据库服务器上运行时以与此操作符相同的方式评估两个 Python 值。对于在 Python 中进行 SQL  表达式评估函数非常有用,例如用于 ORM 混合属性的函数,以及用于在多行更新或删除后匹配会话中的对象的 ORM“评估器”。
    例如:
>>> expr = column('x').op('+', python_impl=lambda a, b: a + b)('y')
  • 上述表达式的操作符也适用于非 SQL 左右对象:
>>> expr.operator(5, 10)
15
  • 2.0 版中的新功能。

参见

Operators.bool_op()

重新定义和创建新操作符

在连接条件中使用自定义操作符

method operate(op: OperatorType, *other: Any, **kwargs: Any) → ColumnElement[Any]

对参数进行操作。

这是操作的最低级别,默认情况下会引发NotImplementedError

在子类上覆盖这个方法可以使常见行为应用于所有操作。例如,覆盖ColumnOperators以将func.lower()应用于左右两侧:

class MyComparator(ColumnOperators):
    def operate(self, op, other, **kwargs):
        return op(func.lower(self), func.lower(other), **kwargs)

参数:

  • op – 操作符可调用。
  • *other – 操作的“其他”一侧。对于大多数操作,将是单个标量。
  • **kwargs – 修饰符。这些可以由特殊操作符传递,例如ColumnOperators.contains()
method regexp_match(pattern: Any, flags: str | None = None) → ColumnOperators

继承自 ColumnOperators.regexp_match() 方法的 ColumnOperators

实现了一个特定于数据库的‘regexp match’操作符。

例如:

stmt = select(table.c.some_column).where(
    table.c.some_column.regexp_match('^(b|c)')
)

ColumnOperators.regexp_match() 尝试解析为后端提供的类似 REGEXP 的函数或操作符,但具体的正则表达式语法和可用标志不是后端通用的

例如:

  • PostgreSQL - 在否定时呈现x ~ yx !~ y
  • Oracle - 呈现REGEXP_LIKE(x, y)
  • SQLite - 使用了 SQLite 的REGEXP占位符操作符,并调用了 Python 的re.match()内置函数。
  • 其他后端可能提供特殊实现。
  • 没有任何特殊实现的后端将发出操作符“REGEXP”或“NOT REGEXP”。例如,这与 SQLite 和 MySQL 兼容。

目前正则表达式支持已实现在 Oracle、PostgreSQL、MySQL 和 MariaDB 中。对于 SQLite,部分支持可用。第三方方言中的支持可能有所不同。

参数:

  • pattern – 正则表达式模式字符串或列子句。
  • flags – 要应用的任何正则表达式字符串标志,仅作为普通 Python 字符串传递。这些标志是特定于后端的。一些后端,如 PostgreSQL 和  MariaDB,可能会将标志作为模式的一部分指定。在 PostgreSQL 中使用忽略大小写标志‘i’时,将使用忽略大小写的正则表达式匹配操作符~*!~*

版本 1.4 中新增。

在版本 1.4.48 更改为:2.0.18 请注意,由于实现错误,先前的“flags”参数接受了 SQL  表达式对象,例如列表达式,而不仅仅是普通的 Python  字符串。这种实现与缓存不兼容,并已被移除;“flags”参数应该只传递字符串,因为这些标志将作为 SQL 表达式中的文字内联值呈现。

另请参阅

ColumnOperators.regexp_replace()

method regexp_replace(pattern: Any, replacement: Any, flags: str | None = None) → ColumnOperators

继承自 ColumnOperators.regexp_replace() 方法的 ColumnOperators

实现了一个特定于数据库的‘regexp replace’操作符。

例如:

stmt = select(
    table.c.some_column.regexp_replace(
        'b(..)',
        'XY',
        flags='g'
    )
)

ColumnOperators.regexp_replace() 尝试解析为后端提供的类似 REGEXP_REPLACE 的函数,通常会生成函数 REGEXP_REPLACE()。然而,具体的正则表达式语法和可用标志与后端无关

目前针对 Oracle、PostgreSQL、MySQL 8 或更高版本以及 MariaDB 实现了正则表达式替换支持。第三方方言中的支持可能有所不同。

参数:

  • pattern – 正则表达式模式字符串或列子句。
  • pattern – 替换字符串或列子句。
  • flags – 要应用的任何正则表达式字符串标志,仅作为纯 Python 字符串传递。这些标志是后端特定的。某些后端,如 PostgreSQL 和 MariaDB,也可以将标志作为模式的一部分指定。

1.4 版中新增。

从版本 1.4.48 更改,: 2.0.18 请注意,由于实现错误,以前“flags”参数接受 SQL  表达式对象,如列表达式,而不仅仅是纯 Python 字符串。此实现与缓存不正确,已删除;仅应传递字符串作为“flags”参数,因为这些标志在  SQL 表达式中被呈现为文字内联值。

另请参阅

ColumnOperators.regexp_match()

attribute remote_attr

继承自 AssociationProxyInstance.remote_attr 属性的 AssociationProxyInstance

AssociationProxyInstance 引用的“remote”类属性。

另请参阅

AssociationProxyInstance.attr

AssociationProxyInstance.local_attr

method reverse_operate(op: OperatorType, other: Any, **kwargs: Any) → Operators

继承自 Operators.reverse_operate() 方法的 Operators

对参数进行反向操作。

使用方式与 operate() 相同

attribute scalar

继承自 AssociationProxyInstance.scalar 属性的 AssociationProxyInstance

如果此 AssociationProxyInstance 代理一个本地方的标量关系,则返回 True

method startswith(other: Any, escape: str | None = None, autoescape: bool = False) → ColumnOperators

继承自 ColumnOperators.startswith() 方法的 ColumnOperators

实现 startswith 运算符。

产生一个 LIKE 表达式,用于测试字符串值的起始匹配:

column LIKE <other> || '%'

例如:

stmt = select(sometable).\
    where(sometable.c.column.startswith("foobar"))

由于该运算符使用 LIKE,所以存在于 表达式内部的通配符字符 "%""_" 也将像通配符一样工作。对于文字字符串值,可以将 ColumnOperators.startswith.autoescape 标志设置为 True,以将这些字符的出现转义为它们自身,而不是作为通配符字符进行匹配。或者,ColumnOperators.startswith.escape 参数将建立一个给定的字符作为转义字符,当目标表达式不是文字字符串时可能会有用。

参数:

  • other – 待比较的表达式。通常是一个普通字符串值,但也可以是任意的 SQL 表达式。LIKE 通配符字符 %_ 默认情况下不会被转义,除非 ColumnOperators.startswith.autoescape 标志设置为 True。
  • autoescape
    布尔值;当为 True 时,在 LIKE 表达式中建立转义字符,然后将其应用于比较值中所有出现的 "%""_" 和转义字符本身,假设比较值是一个文字字符串而不是一个 SQL 表达式。
    一个表达式,例如:
somecolumn.startswith("foo%bar", autoescape=True)
  • 将呈现为:
somecolumn LIKE :param || '%' ESCAPE '/'
  • :param 的值为 "foo/%bar"
  • escape
    一个给定的字符,当给出时将使用 ESCAPE 关键字来建立该字符作为转义字符。然后可以将此字符放置在 %_ 的前面,以允许它们像自己一样起作用,而不是通配符字符。
    一个表达式,例如:
somecolumn.startswith("foo/%bar", escape="^")
  • 将呈现为:
somecolumn LIKE :param || '%' ESCAPE '^'
  • 参数也可以与ColumnOperators.startswith.autoescape结合使用:
somecolumn.startswith("foo%bar^bat", escape="^", autoescape=True)
  • 在上述情况下,给定的文字参数将在传递到数据库之前转换为"foo^%bar^^bat"

另请参阅

ColumnOperators.endswith()

ColumnOperators.contains()

ColumnOperators.like()

attribute target_class: Type[Any]

由此AssociationProxyInstance处理的中间类。

拦截的追加/设置/赋值事件将导致生成此类的新实例。

attribute timetuple: Literal[None] = None

继承自 ColumnOperators ColumnOperators.timetuple 属性

Hack,允许在左侧比较日期时间对象。

class sqlalchemy.ext.associationproxy.AssociationProxyExtensionType

一个枚举。

成员

ASSOCIATION_PROXY

类签名

sqlalchemy.ext.associationproxy.AssociationProxyExtensionTypesqlalchemy.orm.base.InspectionAttrExtensionType

attribute ASSOCIATION_PROXY = 'ASSOCIATION_PROXY'

表示一个InspectionAttr的符号,其类型为AssociationProxy

赋予InspectionAttr.extension_type属性。

简化标量集合

考虑两个类UserKeyword之间的多对多映射。每个User可以拥有任意数量的Keyword对象,反之亦然(多对多模式在多对多中有描述)。下面的示例以相同的��式说明了这种模式,只是在User类中添加了一个名为User.keywords的额外属性:

from __future__ import annotations
from typing import Final
from typing import List
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.associationproxy import AssociationProxy
class Base(DeclarativeBase):
    pass
class User(Base):
    __tablename__ = "user"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(64))
    kw: Mapped[List[Keyword]] = relationship(secondary=lambda: user_keyword_table)
    def __init__(self, name: str):
        self.name = name
    # proxy the 'keyword' attribute from the 'kw' relationship
    keywords: AssociationProxy[List[str]] = association_proxy("kw", "keyword")
class Keyword(Base):
    __tablename__ = "keyword"
    id: Mapped[int] = mapped_column(primary_key=True)
    keyword: Mapped[str] = mapped_column(String(64))
    def __init__(self, keyword: str):
        self.keyword = keyword
user_keyword_table: Final[Table] = Table(
    "user_keyword",
    Base.metadata,
    Column("user_id", Integer, ForeignKey("user.id"), primary_key=True),
    Column("keyword_id", Integer, ForeignKey("keyword.id"), primary_key=True),
)

在上面的示例中,association_proxy()应用于User类,以生成kw关系的“视图”,该视图公开与每个Keyword对象关联的.keyword的字符串值。当向集合添加字符串时,它还会透明地创建新的Keyword对象:

>>> user = User("jek")
>>> user.keywords.append("cheese-inspector")
>>> user.keywords.append("snack-ninja")
>>> print(user.keywords)
['cheese-inspector', 'snack-ninja']

要理解这一机制,首先回顾一下在不使用.keywords关联代理的情况下,UserKeyword的行为。通常,读取和操作与User相关联的“关键词”字符串集合需要从每个集合元素遍历到.keyword属性,这可能很麻烦。下面的示例说明了在不使用关联代理的情况下应用的相同一系列操作:

>>> # identical operations without using the association proxy
>>> user = User("jek")
>>> user.kw.append(Keyword("cheese-inspector"))
>>> user.kw.append(Keyword("snack-ninja"))
>>> print([keyword.keyword for keyword in user.kw])
['cheese-inspector', 'snack-ninja']

association_proxy()函数产生的AssociationProxy对象是Python 描述符的一个实例,并且不以任何方式被Mapper“映射”。因此,无论是使用 Declarative 还是 Imperative 映射,它都始终在映射类的类定义中内联指示。

该代理通过响应操作来操作底层的映射属性或集合,并且通过代理进行的更改立即反映在映射属性中,反之亦然。底层属性仍然可以完全访问。

当首次访问时,关联代理会对目标集合执行内省操作,以便其行为正确对应。诸如本地代理的属性是否为集合(通常情况下)或标量引用,以及集合是否像集合、列表或字典一样操作等细节都会考虑在内,以便代理的行为应该与底层集合或属性的行为一样。

创造新价值

当关联代理拦截到列表append()事件(或集合add(),字典__setitem__()或标量赋值事件)时,它会使用其构造函数实例化“中介”对象的新实例,将给定值作为单个参数传递。在我们上面的示例中,像下面这样的操作:

user.keywords.append("cheese-inspector")

被关联代理翻译成操作:

user.kw.append(Keyword("cheese-inspector"))

这个示例在这里起作用,因为我们设计了Keyword的构造函数以接受一个单一的位置参数,keyword。 对于那些单参数构造函数不可行的情况,可以使用association_proxy.creator参数自定义关联代理的创建行为,该参数引用一个可调用对象(即 Python 函数),该对象将根据单个参数生成一个新的对象实例。 下面我们使用通常的 lambda 来说明这一点:

class User(Base):
    ...
    # use Keyword(keyword=kw) on append() events
    keywords: AssociationProxy[List[str]] = association_proxy(
        "kw", "keyword", creator=lambda kw: Keyword(keyword=kw)
    )

在列表或集合类型的集合或标量属性的情况下,creator函数接受一个参数。 在基于字典的集合的情况下,它接受两个参数,“key”和“value”。 下面的示例在代理到基于字典的集合中给出。

当关联代理拦截到列表append()事件(或集合add(),字典__setitem__()或标量赋值事件)时,它会使用其构造函数实例化一个新的“中间”对象的实例,将给定的值作为单个参数传递。 在上面的示例中,像这样的操作:

user.keywords.append("cheese-inspector")

由关联代理转换为操作:

user.kw.append(Keyword("cheese-inspector"))

这个示例在这里起作用,因为我们设计了Keyword的构造函数以接受一个单一的位置参数,keyword。 对于那些单参数构造函数不可行的情况,可以使用association_proxy.creator参数自定义关联代理的创建行为,该参数引用一个可调用对象(即 Python 函数),该对象将根据单个参数生成一个新的对象实例。 下面我们使用通常的 lambda 来说明这一点:

class User(Base):
    ...
    # use Keyword(keyword=kw) on append() events
    keywords: AssociationProxy[List[str]] = association_proxy(
        "kw", "keyword", creator=lambda kw: Keyword(keyword=kw)
    )

在列表或集合类型的集合或标量属性的情况下,creator函数接受一个参数。 在基于字典的集合的情况下,它接受两个参数,“key”和“value”。 下面的示例在代理到基于字典的集合中给出。

简化关联对象

“关联对象”模式是多对多关系的扩展形式,并在关联对象中进行了描述。 在常规使用过程中,关联代理对于保持“关联对象”不被干扰非常有用。

假设我们上面的user_keyword表有额外的列,我们希望显式映射这些列,但在大多数情况下我们不需要直接访问这些属性。下面,我们展示一个新的映射,引入了UserKeywordAssociation类,该类映射到前面展示的user_keyword表。这个类添加了一个额外的列special_key,我们偶尔需要访问这个值,但通常不需要。我们在User类上创建了一个名为keywords的关联代理,它将连接Useruser_keyword_associations集合与每个UserKeywordAssociation上存在的.keyword属性之间的差距:

from __future__ import annotations
from typing import List
from typing import Optional
from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.associationproxy import AssociationProxy
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
    pass
class User(Base):
    __tablename__ = "user"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(64))
    user_keyword_associations: Mapped[List[UserKeywordAssociation]] = relationship(
        back_populates="user",
        cascade="all, delete-orphan",
    )
    # association proxy of "user_keyword_associations" collection
    # to "keyword" attribute
    keywords: AssociationProxy[List[Keyword]] = association_proxy(
        "user_keyword_associations",
        "keyword",
        creator=lambda keyword_obj: UserKeywordAssociation(keyword=keyword_obj),
    )
    def __init__(self, name: str):
        self.name = name
class UserKeywordAssociation(Base):
    __tablename__ = "user_keyword"
    user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), primary_key=True)
    keyword_id: Mapped[int] = mapped_column(ForeignKey("keyword.id"), primary_key=True)
    special_key: Mapped[Optional[str]] = mapped_column(String(50))
    user: Mapped[User] = relationship(back_populates="user_keyword_associations")
    keyword: Mapped[Keyword] = relationship()
class Keyword(Base):
    __tablename__ = "keyword"
    id: Mapped[int] = mapped_column(primary_key=True)
    keyword: Mapped[str] = mapped_column("keyword", String(64))
    def __init__(self, keyword: str):
        self.keyword = keyword
    def __repr__(self) -> str:
        return f"Keyword({self.keyword!r})"

使用上述配置,我们可以操作每个User对象的.keywords集合,每个对象都公开了从底层UserKeywordAssociation元素获取的Keyword对象集合:

>>> user = User("log")
>>> for kw in (Keyword("new_from_blammo"), Keyword("its_big")):
...     user.keywords.append(kw)
>>> print(user.keywords)
[Keyword('new_from_blammo'), Keyword('its_big')]

这个例子与之前在 Simplifying Scalar Collections 中展示的例子形成对比,在那个例子中,关联代理公开了一个字符串集合,而不是一个组合对象集合。在这种情况下,每个.keywords.append()操作等同于:

>>> user.user_keyword_associations.append(
...     UserKeywordAssociation(keyword=Keyword("its_heavy"))
... )

UserKeywordAssociation对象有两个属性,这两个属性都在关联代理的append()操作范围内填充;.keyword指的是Keyword对象,.user指的是User对象。首先填充.keyword属性,因为关联代理响应.append()操作生成一个新的UserKeywordAssociation对象,将给定的Keyword实例分配给.keyword属性。然后,由于UserKeywordAssociation对象被追加到User.user_keyword_associations集合中,为User.user_keyword_associations配置为back_populatesUserKeywordAssociation.user属性在给定的UserKeywordAssociation实例上初始化,以指向接收追加操作的父User。上面的special_key参数保持其默认值为None

对于那些我们确实希望special_key有一个值的情况,我们显式创建UserKeywordAssociation对象。下面我们分配了所有三个属性,其中在构造过程中分配.user的效果是将新的UserKeywordAssociation追加到User.user_keyword_associations集合(通过关系):

>>> UserKeywordAssociation(
...     keyword=Keyword("its_wood"), user=user, special_key="my special key"
... )

关联代理通过以下所有操作返回给我们一个由Keyword对象表示的集合:

>>> print(user.keywords)
[Keyword('new_from_blammo'), Keyword('its_big'), Keyword('its_heavy'), Keyword('its_wood')]

代理到基于字典的集合

关联代理也可以代理基于字典的集合。SQLAlchemy 映射通常使用attribute_keyed_dict()集合类型来创建字典集合,以及自定义基于字典的集合中描述的扩展技术。

当检测到使用基于字典的集合时,关联代理会调整其行为。当新值添加到字典中时,关联代理通过将两个参数传递给创建函数而不是一个参数来实例化中间对象,即键和值。与往常一样,这个创建函数默认为中间类的构造函数,并且可以使用creator参数进行定制。

下面,我们修改了我们的UserKeywordAssociation示例,使得User.user_keyword_associations集合现在将使用字典映射,其中UserKeywordAssociation.special_key参数将用作字典的键。我们还将User.keywords代理应用了creator参数,以便在向字典添加新元素时适当地分配这些值:

from __future__ import annotations
from typing import Dict
from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.associationproxy import AssociationProxy
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm.collections import attribute_keyed_dict
class Base(DeclarativeBase):
    pass
class User(Base):
    __tablename__ = "user"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(64))
    # user/user_keyword_associations relationship, mapping
    # user_keyword_associations with a dictionary against "special_key" as key.
    user_keyword_associations: Mapped[Dict[str, UserKeywordAssociation]] = relationship(
        back_populates="user",
        collection_class=attribute_keyed_dict("special_key"),
        cascade="all, delete-orphan",
    )
    # proxy to 'user_keyword_associations', instantiating
    # UserKeywordAssociation assigning the new key to 'special_key',
    # values to 'keyword'.
    keywords: AssociationProxy[Dict[str, Keyword]] = association_proxy(
        "user_keyword_associations",
        "keyword",
        creator=lambda k, v: UserKeywordAssociation(special_key=k, keyword=v),
    )
    def __init__(self, name: str):
        self.name = name
class UserKeywordAssociation(Base):
    __tablename__ = "user_keyword"
    user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), primary_key=True)
    keyword_id: Mapped[int] = mapped_column(ForeignKey("keyword.id"), primary_key=True)
    special_key: Mapped[str]
    user: Mapped[User] = relationship(
        back_populates="user_keyword_associations",
    )
    keyword: Mapped[Keyword] = relationship()
class Keyword(Base):
    __tablename__ = "keyword"
    id: Mapped[int] = mapped_column(primary_key=True)
    keyword: Mapped[str] = mapped_column(String(64))
    def __init__(self, keyword: str):
        self.keyword = keyword
    def __repr__(self) -> str:
        return f"Keyword({self.keyword!r})"

我们将.keywords集合说明为一个字典,将UserKeywordAssociation.special_key值映射到Keyword对象:

>>> user = User("log")
>>> user.keywords["sk1"] = Keyword("kw1")
>>> user.keywords["sk2"] = Keyword("kw2")
>>> print(user.keywords)
{'sk1': Keyword('kw1'), 'sk2': Keyword('kw2')}

组合关联代理

考虑到我们之前的从关系到标量属性的代理示例,跨关联对象进行代理,以及代理字典的示例,我们可以将所有三种技术结合起来,为User提供一个严格处理special_key字符串值映射到字符串keywordkeywords字典。UserKeywordAssociationKeyword类都完全隐藏了。这是通过在User上构建一个关联代理来实现的,该代理指向UserKeywordAssociation上存在的关联代理:

from __future__ import annotations
from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.associationproxy import AssociationProxy
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm.collections import attribute_keyed_dict
class Base(DeclarativeBase):
    pass
class User(Base):
    __tablename__ = "user"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(64))
    user_keyword_associations: Mapped[Dict[str, UserKeywordAssociation]] = relationship(
        back_populates="user",
        collection_class=attribute_keyed_dict("special_key"),
        cascade="all, delete-orphan",
    )
    # the same 'user_keyword_associations'->'keyword' proxy as in
    # the basic dictionary example.
    keywords: AssociationProxy[Dict[str, str]] = association_proxy(
        "user_keyword_associations",
        "keyword",
        creator=lambda k, v: UserKeywordAssociation(special_key=k, keyword=v),
    )
    def __init__(self, name: str):
        self.name = name
class UserKeywordAssociation(Base):
    __tablename__ = "user_keyword"
    user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), primary_key=True)
    keyword_id: Mapped[int] = mapped_column(ForeignKey("keyword.id"), primary_key=True)
    special_key: Mapped[str] = mapped_column(String(64))
    user: Mapped[User] = relationship(
        back_populates="user_keyword_associations",
    )
    # the relationship to Keyword is now called
    # 'kw'
    kw: Mapped[Keyword] = relationship()
    # 'keyword' is changed to be a proxy to the
    # 'keyword' attribute of 'Keyword'
    keyword: AssociationProxy[Dict[str, str]] = association_proxy("kw", "keyword")
class Keyword(Base):
    __tablename__ = "keyword"
    id: Mapped[int] = mapped_column(primary_key=True)
    keyword: Mapped[str] = mapped_column(String(64))
    def __init__(self, keyword: str):
        self.keyword = keyword

User.keywords现在是一个字符串到字符串的字典,其中UserKeywordAssociationKeyword对象被透明地创建和删除,使用关联代理。在下面的示例中,我们说明了使用赋值运算符的用法,这也由关联代理适当处理,一次将字典值应用到集合中:

>>> user = User("log")
>>> user.keywords = {"sk1": "kw1", "sk2": "kw2"}
>>> print(user.keywords)
{'sk1': 'kw1', 'sk2': 'kw2'}
>>> user.keywords["sk3"] = "kw3"
>>> del user.keywords["sk2"]
>>> print(user.keywords)
{'sk1': 'kw1', 'sk3': 'kw3'}
>>> # illustrate un-proxied usage
... print(user.user_keyword_associations["sk3"].kw)
<__main__.Keyword object at 0x12ceb90>

上面示例的一个注意事项是,因为对每个字典设置操作都会创建Keyword对象,所以示例无法保持Keyword对象在其字符串名称上的唯一性,这是像这样的标记场景的典型要求。对于这种用例,推荐使用UniqueObject这样的配方,或者类似的创建策略,它将对Keyword类的构造函数应用“先查找,然后创建”的策略,以便如果给定名称已经存在,则返回已存在的Keyword

使用关联代理进行查询

AssociationProxy 具有简单的 SQL 构建能力,其工作方式类似于其他 ORM 映射的属性,并提供基于 SQL EXISTS 关键字的基本过滤支持。

注意

关联代理扩展的主要目的是允许改进对已加载的映射对象实例的持久性和对象访问模式。类绑定查询功能的用途有限,并不会取代在构建具有 JOIN、急加载选项等 SQL 查询时引用底层属性的需要。

对于这一部分,请假设一个既有关联代理指向列,又有关联代理指向相关对象的类,就像下面的示例映射一样:

from __future__ import annotations
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
from sqlalchemy.orm import DeclarativeBase, relationship
from sqlalchemy.orm.collections import attribute_keyed_dict
from sqlalchemy.orm.collections import Mapped
class Base(DeclarativeBase):
    pass
class User(Base):
    __tablename__ = "user"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(64))
    user_keyword_associations: Mapped[UserKeywordAssociation] = relationship(
        cascade="all, delete-orphan",
    )
    # object-targeted association proxy
    keywords: AssociationProxy[List[Keyword]] = association_proxy(
        "user_keyword_associations",
        "keyword",
    )
    # column-targeted association proxy
    special_keys: AssociationProxy[List[str]] = association_proxy(
        "user_keyword_associations", "special_key"
    )
class UserKeywordAssociation(Base):
    __tablename__ = "user_keyword"
    user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), primary_key=True)
    keyword_id: Mapped[int] = mapped_column(ForeignKey("keyword.id"), primary_key=True)
    special_key: Mapped[str] = mapped_column(String(64))
    keyword: Mapped[Keyword] = relationship()
class Keyword(Base):
    __tablename__ = "keyword"
    id: Mapped[int] = mapped_column(primary_key=True)
    keyword: Mapped[str] = mapped_column(String(64))

生成的 SQL 采用针对 EXISTS SQL 操作符的相关子查询形式,以便可以在不需要对封闭查询进行其他修改的情况下在 WHERE 子句中使用。如果关联代理的直接目标是映射的列表达式,则可以使用标准列操作符,这些操作符将嵌入在子查询中。例如,一个直接的等式操作符:

>>> print(session.scalars(select(User).where(User.special_keys == "jek")))
SELECT  "user".id  AS  user_id,  "user".name  AS  user_name
FROM  "user"
WHERE  EXISTS  (SELECT  1
FROM  user_keyword
WHERE  "user".id  =  user_keyword.user_id  AND  user_keyword.special_key  =  :special_key_1) 

一个 LIKE 操作符:

>>> print(session.scalars(select(User).where(User.special_keys.like("%jek"))))
SELECT  "user".id  AS  user_id,  "user".name  AS  user_name
FROM  "user"
WHERE  EXISTS  (SELECT  1
FROM  user_keyword
WHERE  "user".id  =  user_keyword.user_id  AND  user_keyword.special_key  LIKE  :special_key_1) 

对于关联代理,其直接目标是相关对象或集合,或相关对象上的另一个关联代理或属性的情况,可以使用与关系相关的操作符,例如PropComparator.has()PropComparator.any()User.keywords属性实际上是两个关联代理链接在一起,因此在使用该代理生成 SQL 短语时,我们得到两个级别的 EXISTS 子查询:

>>> print(session.scalars(select(User).where(User.keywords.any(Keyword.keyword == "jek"))))
SELECT  "user".id  AS  user_id,  "user".name  AS  user_name
FROM  "user"
WHERE  EXISTS  (SELECT  1
FROM  user_keyword
WHERE  "user".id  =  user_keyword.user_id  AND  (EXISTS  (SELECT  1
FROM  keyword
WHERE  keyword.id  =  user_keyword.keyword_id  AND  keyword.keyword  =  :keyword_1))) 

这不是最有效的 SQL 形式,因此虽然关联代理可以方便快速生成 WHERE 条件,但应该检查 SQL 结果并将其“展开”为显式 JOIN 条件以获得最佳使用,特别是当将关联代理链接在一起时。

版本 1.3 中的更改:根据目标类型,关联代理现在提供不同的查询模式。请参阅 AssociationProxy 现在为面向列的目标提供标准列操作符。


SqlAlchemy 2.0 中文文档(二十九)(4)https://developer.aliyun.com/article/1560467

相关文章
|
2天前
|
SQL 数据库 Python
SqlAlchemy 2.0 中文文档(二十六)(4)
SqlAlchemy 2.0 中文文档(二十六)
13 2
|
2天前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(二十九)(2)
SqlAlchemy 2.0 中文文档(二十九)
20 7
|
2天前
|
SQL 存储 关系型数据库
SqlAlchemy 2.0 中文文档(二十九)(1)
SqlAlchemy 2.0 中文文档(二十九)
16 4
|
2天前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(二十九)(4)
SqlAlchemy 2.0 中文文档(二十九)
16 4
|
2天前
|
SQL 前端开发 关系型数据库
SqlAlchemy 2.0 中文文档(二十七)(2)
SqlAlchemy 2.0 中文文档(二十七)
14 2
|
2天前
|
SQL 缓存 前端开发
SqlAlchemy 2.0 中文文档(二十七)(5)
SqlAlchemy 2.0 中文文档(二十七)
10 2
|
2天前
|
自然语言处理 数据库 Python
SqlAlchemy 2.0 中文文档(二十六)(2)
SqlAlchemy 2.0 中文文档(二十六)
12 2
|
2天前
|
SQL 缓存 数据库连接
SqlAlchemy 2.0 中文文档(二十六)(3)
SqlAlchemy 2.0 中文文档(二十六)
11 2
|
2天前
|
SQL 缓存 数据库连接
SqlAlchemy 2.0 中文文档(二十六)(1)
SqlAlchemy 2.0 中文文档(二十六)
12 2
|
2天前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(二十九)(5)
SqlAlchemy 2.0 中文文档(二十九)
11 1