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.ColumnAssociationProxyInstance
(sqlalchemy.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_attr
和 AssociationProxyInstance.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 ~ y
或x !~ 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.AssociationProxyExtensionType
(sqlalchemy.orm.base.InspectionAttrExtensionType
)
attribute ASSOCIATION_PROXY = 'ASSOCIATION_PROXY'
表示一个InspectionAttr
的符号,其类型为AssociationProxy
。
赋予InspectionAttr.extension_type
属性。
简化标量集合
考虑两个类User
和Keyword
之间的多对多映射。每个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
关联代理的情况下,User
和Keyword
的行为。通常,读取和操作与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
的关联代理,它将连接User
的user_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_populates
的UserKeywordAssociation.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
字符串值映射到字符串keyword
的keywords
字典。UserKeywordAssociation
和Keyword
类都完全隐藏了。这是通过在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
现在是一个字符串到字符串的字典,其中UserKeywordAssociation
和Keyword
对象被透明地创建和删除,使用关联代理。在下面的示例中,我们说明了使用赋值运算符的用法,这也由关联代理适当处理,一次将字典值应用到集合中:
>>> 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