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

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


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

自定义类型

原文:docs.sqlalchemy.org/en/20/core/custom_types.html

存在各种方法来重新定义现有类型的行为以及提供新类型。

覆盖类型编译

经常需要强制类型的“字符串”版本,即在 CREATE TABLE 语句或其他 SQL 函数(如 CAST)中呈现的版本进行更改。例如,应用程序可能希望强制在除一个平台外的所有平台上呈现BINARY,在该平台上希望呈现BLOB。对于大多数用例,首选使用现有的通用类型,例如LargeBinary。但为了更准确地控制类型,可以将每个方言的编译指令与任何类型关联起来:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.types import BINARY
@compiles(BINARY, "sqlite")
def compile_binary_sqlite(type_, compiler, **kw):
    return "BLOB"

上述代码允许使用BINARY,它将针对除 SQLite 外的所有后端生成字符串BINARY,在 SQLite 的情况下,它将生成BLOB

请参阅更改类型编译部分,这是自定义 SQL 构造和编译扩展的一个子部分,其中包含额外的示例。

增强现有类型

TypeDecorator允许创建自定义类型,为现有类型对象添加绑定参数和结果处理行为。当需要对数据进行额外的 Python 内部编组以及/或从数据库中进行时使用。

注意

TypeDecorator的绑定和结果处理是额外的,除了由托管类型已执行的处理外,SQLAlchemy 还会根据每个 DBAPI 定制来执行特定于该 DBAPI 的处理。虽然可以通过直接子类化来替换给定类型的处理,但在实践中从不需要,并且 SQLAlchemy 不再支持这作为公共用例。

对象名称 描述
TypeDecorator 允许创建类型,为现有类型添加额外功能。
class sqlalchemy.types.TypeDecorator

允许创建类型,为现有类型添加额外功能。

此方法优于直接子类化 SQLAlchemy 内置类型,因为它确保保留底层类型的所有必需功能。

典型用法:

import sqlalchemy.types as types
class MyType(types.TypeDecorator):
  '''Prefixes Unicode values with "PREFIX:" on the way in and
 strips it off on the way out.
 '''
    impl = types.Unicode
    cache_ok = True
    def process_bind_param(self, value, dialect):
        return "PREFIX:" + value
    def process_result_value(self, value, dialect):
        return value[7:]
    def copy(self, **kw):
        return MyType(self.impl.length)

类级别的impl属性是必需的,并且可以引用任何TypeEngine类。或者,可以使用load_dialect_impl()方法根据给定的方言提供不同的类型类;在这种情况下,impl变量可以引用TypeEngine作为占位符。

TypeDecorator.cache_ok类级别标志指示此自定义TypeDecorator是否可以安全地用作缓存键的一部分。此标志默认为None,当 SQL 编译器尝试为使用此类型的语句生成缓存键时,将最初生成警告。如果TypeDecorator不能保证每次都产生相同的绑定/结果行为和 SQL 生成,则应将此标志设置为False;否则,如果该类每次都产生相同的行为,则可以设置为True。有关此工作原理的更多说明,请参见TypeDecorator.cache_ok

接收不类似于最终使用的类型的 Python 类型的类型可能希望定义TypeDecorator.coerce_compared_value()方法。这用于在表达式中将 Python 对象强制转换为绑定参数时给表达式系统一个提示。考虑这个表达式:

mytable.c.somecol + datetime.date(2009, 5, 15)

在上面,如果“somecol”是一个Integer变体,我们做日期算术操作是有意义的,其中上面通常被数据库解释为将一些天数加到给定日期上。表达式系统通过不试图将“date()”值强制转换为面向整数的绑定参数来做正确的事情。

但是,在TypeDecorator的情况下,我们通常会将一个传入的 Python 类型更改为新的东西 - 默认情况下,TypeDecorator会将非类型化的一侧“强制”成与自身相同的类型。例如下面,我们定义了一个将日期值存储为整数的“epoch”类型:

class MyEpochType(types.TypeDecorator):
    impl = types.Integer
    cache_ok = True
    epoch = datetime.date(1970, 1, 1)
    def process_bind_param(self, value, dialect):
        return (value - self.epoch).days
    def process_result_value(self, value, dialect):
        return self.epoch + timedelta(days=value)

使用上述类型的somecol + date表达式将会强制右侧的“date”也被视为MyEpochType

通过TypeDecorator.coerce_compared_value()方法可以覆盖此行为,该方法返回一个应用于表达式值的类型。在下面的示例中,我们设置了一个整数值将被视为Integer,而任何其他值都被假定为日期并将被视为MyEpochType

def coerce_compared_value(self, op, value):
    if isinstance(value, int):
        return Integer()
    else:
        return self

警告

注意,coerce_compared_value 的行为不会默认从基本类型那里继承。如果 TypeDecorator 是增强某种类型需要特殊逻辑的装饰器,这个方法 必须 被重写。一个关键的例子是当装饰 JSONJSONB 类型时;应该使用 TypeEngine.coerce_compared_value() 的默认规则来处理像索引操作这样的操作符:

from sqlalchemy import JSON
from sqlalchemy import TypeDecorator
class MyJsonType(TypeDecorator):
    impl = JSON
    cache_ok = True
    def coerce_compared_value(self, op, value):
        return self.impl.coerce_compared_value(op, value)

没有上述步骤,索引操作,比如mycol['foo']会导致索引值'foo'被 JSON 编码。

类似地,当使用 ARRAY 数据类型时,索引操作的类型强制转换(例如 mycol[5])也由 TypeDecorator.coerce_compared_value() 处理,再次简单的重写就足够了,除非对特定操作符需要特殊规则:

from sqlalchemy import ARRAY
from sqlalchemy import TypeDecorator
class MyArrayType(TypeDecorator):
    impl = ARRAY
    cache_ok = True
    def coerce_compared_value(self, op, value):
        return self.impl.coerce_compared_value(op, value)

成员

cache_ok, operate(), reverse_operate(), init(),  bind_expression(), bind_processor(), coerce_compared_value(),  coerce_to_is_types, column_expression(), comparator_factory,  compare_values(), copy(), get_dbapi_type(), literal_processor(),  load_dialect_impl(), process_bind_param(), process_literal_param(),  process_result_value(), result_processor(), sort_key_function,  type_engine()

类签名

sqlalchemy.types.TypeDecorator (sqlalchemy.sql.expression.SchemaEventTarget, sqlalchemy.types.ExternalType, sqlalchemy.types.TypeEngine)

attribute cache_ok: bool | None = None

继承自 ExternalType.cache_ok 属性ExternalType

使用此 ExternalType 表示的 if 语句是否“可以缓存”。

默认值 None 会发出警告,然后不允许缓存包含此类型的语句。将其设置为 False 可以禁用使用此类型的语句的缓存,而不发出警告。当设置为 True 时,对象的类和其状态的选定元素将用作缓存键的一部分。例如,使用 TypeDecorator

class MyType(TypeDecorator):
    impl = String
    cache_ok = True
    def __init__(self, choices):
        self.choices = tuple(choices)
        self.internal_only = True

上述类型的缓存键将等同于:

>>> MyType(["a", "b", "c"])._static_cache_key
(<class '__main__.MyType'>, ('choices', ('a', 'b', 'c')))

缓存方案将从类型中提取与 __init__() 方法中参数名称相对应的属性。在上面的例子中,“choices” 属性成为缓存键的一部分,但“internal_only” 不会,因为没有名为 “internal_only” 的参数。

可缓存元素的要求是它们是可哈希的,并且还要求对于给定缓存值,它们每次都指示使用此类型的表达式的相同 SQL 渲染。

为了适应引用不可哈希结构(如字典、集合和列表)的数据类型,可以通过将可哈希结构分配给其名称与参数名称对应的属性来使这些对象“可缓存”。例如,一个接受查找值字典的数据类型可以将其公布为一系列已排序的元组。给定一个先前不可缓存的类型如下:

class LookupType(UserDefinedType):
  '''a custom type that accepts a dictionary as a parameter.
 this is the non-cacheable version, as "self.lookup" is not
 hashable.
 '''
    def __init__(self, lookup):
        self.lookup = lookup
    def get_col_spec(self, **kw):
        return "VARCHAR(255)"
    def bind_processor(self, dialect):
        # ...  works with "self.lookup" ...

其中“lookup”是一个字典。该类型将无法生成缓存键:

>>> type_ = LookupType({"a": 10, "b": 20})
>>> type_._static_cache_key
<stdin>:1: SAWarning: UserDefinedType LookupType({'a': 10, 'b': 20}) will not
produce a cache key because the ``cache_ok`` flag is not set to True.
Set this flag to True if this type object's state is safe to use
in a cache key, or False to disable this warning.
symbol('no_cache')

如果我们确实设置了这样的缓存键,它将无法使用。我们将得到一个包含字典的元组结构,该字典本身无法作为“缓存字典”中的键使用,例如 SQLAlchemy 的语句缓存,因为 Python 字典不可哈希:

>>> # set cache_ok = True
>>> type_.cache_ok = True
>>> # this is the cache key it would generate
>>> key = type_._static_cache_key
>>> key
(<class '__main__.LookupType'>, ('lookup', {'a': 10, 'b': 20}))
>>> # however this key is not hashable, will fail when used with
>>> # SQLAlchemy statement cache
>>> some_cache = {key: "some sql value"}
Traceback (most recent call last): File "<stdin>", line 1,
in <module> TypeError: unhashable type: 'dict'

通过将排序后的元组元组分配给“.lookup”属性,可以使该类型可缓存:

class LookupType(UserDefinedType):
  '''a custom type that accepts a dictionary as a parameter.
 The dictionary is stored both as itself in a private variable,
 and published in a public variable as a sorted tuple of tuples,
 which is hashable and will also return the same value for any
 two equivalent dictionaries.  Note it assumes the keys and
 values of the dictionary are themselves hashable.
 '''
    cache_ok = True
    def __init__(self, lookup):
        self._lookup = lookup
        # assume keys/values of "lookup" are hashable; otherwise
        # they would also need to be converted in some way here
        self.lookup = tuple(
            (key, lookup[key]) for key in sorted(lookup)
        )
    def get_col_spec(self, **kw):
        return "VARCHAR(255)"
    def bind_processor(self, dialect):
        # ...  works with "self._lookup" ...

在上面,LookupType({"a": 10, "b": 20}) 的缓存键将是:

>>> LookupType({"a": 10, "b": 20})._static_cache_key
(<class '__main__.LookupType'>, ('lookup', (('a', 10), ('b', 20))))

新功能,在版本 1.4.14 中:- 为 TypeDecorator 类添加了 cache_ok 标志,以允许对缓存进行一些可配置性。

新版本 1.4.28 中增加了ExternalType mixin,它将cache_ok标志推广到TypeDecoratorUserDefinedType类。

另请参阅

SQL 编译缓存

class Comparator

一个特定于TypeDecoratorComparator

用户定义的TypeDecorator类通常不需要修改此内容。

类签名

sqlalchemy.types.TypeDecorator.Comparatorsqlalchemy.types.Comparator

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

对参数进行操作。

这是最低级的操作,默认情况下引发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 reverse_operate(op: OperatorType, other: Any, **kwargs: Any) → ColumnElement[_CT]

对参数进行反向操作。

使用方式与operate()相同。

method __init__(*args: Any, **kwargs: Any)

构造一个TypeDecorator

发送到这里的参数将传递给分配给impl类级属性的类的构造函数,假设impl是可调用的,并且将生成的对象分配给self.impl实例属性(从而覆盖同名的类属性)。

如果类级impl不是可调用的(不寻常的情况),它将被分配给相同的实例属性,忽略传递给构造函数的参数。

子类可以覆盖此内容以完全自定义self.impl的生成。

method bind_expression(bindparam: BindParameter[_T]) → ColumnElement[_T] | None

给定一个绑定值(即一个BindParameter实例),返回一个 SQL 表达式,该表达式通常将给定参数包装起来。

注意

此方法在语句的SQL 编译阶段调用,当渲染 SQL 字符串时。它不一定针对特定值调用,并且不应与TypeDecorator.process_bind_param()方法混淆,后者是处理语句执行时传递给特定参数的实际值的更典型方法。

TypeDecorator的子类可以重写此方法,以提供类型的自定义绑定表达式行为。此实现将替换基础实现类型的实现。

method bind_processor(dialect: Dialect) → _BindProcessorType[_T] | None

为给定的Dialect提供一个绑定值处理函数。

这是通过TypeEngine.bind_processor()方法通常发生的绑定值转换的方法,它履行了TypeEngine合同。

注意

TypeDecorator 的用户定义的子类不应该实现这个方法,而应该实现TypeDecorator.process_bind_param(),以便保持实现类型提供的“内部”处理。

参数:

dialect – 正在使用的方言实例。

method coerce_compared_value(op: OperatorType | None, value: Any) → Any

在表达式中建议为“强制转换”的 Python 值提供一种类型。

默认情况下,返回 self。当使用此类型的对象在表达式左侧或右侧与尚未分配 SQLAlchemy 类型的普通 Python 对象相比时,表达式系统将调用此方法:

expr = table.c.somecolumn + 35

在上述情况下,如果somecolumn使用此类型,则将使用值operator.add35调用此方法。返回值是为这个特定操作应该使用的 SQLAlchemy 类型。

attribute coerce_to_is_types: Sequence[Type[Any]] = (<class 'NoneType'>,)

指定那些应该在表达式级别强制转换为“IS ”的 Python 类型,当使用==进行比较时(对于!=结合IS NOT也是如此)。

对于大多数 SQLAlchemy 类型,这包括NoneType,以及bool

TypeDecorator 修改此列表,只包括NoneType,因为处理布尔类型的 typedecorator 实现是常见的。

自定义TypeDecorator类可以重写此属性以返回一个空元组,在这种情况下,不会将任何值强制转换为常量。

method column_expression(column: ColumnElement[_T]) → ColumnElement[_T] | None

给定一个 SELECT 列表达式,返回一个包装的 SQL 表达式。

注意

这个方法在语句的SQL 编译阶段调用,当渲染 SQL 字符串时。它不会针对特定值进行调用,并且不应将其与TypeDecorator.process_result_value()方法混淆,后者是处理语句执行后返回的实际值的更典型的方法。

TypeDecorator的子类可以重写此方法,以为类型提供自定义列表达式行为。此实现将替换底层实现类型的实现。

有关方法用途的完整描述,请参阅TypeEngine.column_expression()的描述。

attribute comparator_factory: _ComparatorFactory[Any]

一个Comparator类,将应用于由拥有的ColumnElement对象执行的操作。

当执行列和 SQL 表达式操作时,核心表达式系统会查找comparator_factory属性。当与此属性相关联的是一个Comparator类时,它允许自定义重新定义所有现有运算符,以及定义新的运算符。现有运算符包括通过 Python 运算符重载提供的运算符,如ColumnOperators.__add__()ColumnOperators.__eq__(),以及作为ColumnOperators的标准属性提供的运算符,如ColumnOperators.like()ColumnOperators.in_()

通过简单地对现有类型进行子类化或者使用TypeDecorator,可以允许对这个钩子进行基本的使用。有关示例,请参阅文档中的 Redefining and Creating New Operators 部分。

method compare_values(x: Any, y: Any) → bool

给定两个值,比较它们是否相等。

默认情况下,这将调用底层“impl”的TypeEngine.compare_values(),这通常使用 Python 相等运算符==

此函数由 ORM 用于将原始加载的值与拦截的“更改”值进行比较,以确定是否发生了净变化。

method copy(**kw: Any) → Self

生产这个TypeDecorator实例的副本。

这是一个浅拷贝,并提供了部分TypeEngine合约的实现。通常不需要重写,除非用户定义的TypeDecorator具有应该深拷贝的本地状态。

method get_dbapi_type(dbapi: module) → Any | None

返回由此TypeDecorator表示的 DBAPI 类型对象。

默认情况下,这将调用底层“impl”的TypeEngine.get_dbapi_type()

method literal_processor(dialect: Dialect) → _LiteralProcessorType[_T] | None

为给定的Dialect提供一个字面处理函数。

这是履行通过TypeEngine.literal_processor()方法正常发生的字面值转换的TypeEngine合约的方法。

注意

用户定义的TypeDecorator子类不应该实现此方法,而应该实现TypeDecorator.process_literal_param(),以便维护实现类型提供的“内部”处理。

method load_dialect_impl(dialect: Dialect) → TypeEngine[Any]

返回与方言对应的TypeEngine对象。

这是一个最终用户的覆盖钩子,可用于根据给定的方言提供不同的类型。它被TypeDecorator的实现在帮助确定对于给定的TypeDecorator应最终返回什么类型时使用。

默认情况下返回self.impl

method process_bind_param(value: _T | None, dialect: Dialect) → Any

接收要转换的绑定参数值。

自定义的TypeDecorator子类应该重写此方法,以提供传入数据值的自定义行为。此方法在语句执行时间被调用,并传递要与语句中的绑定参数关联的字面 Python 数据值。

操作可以是任何所需的自定义行为,例如转换或序列化数据。这也可以用作验证逻辑的钩子。

参数:

  • value – 要操作的数据,应为子类中此方法预期的任何类型。可以是None
  • dialect – 使用的Dialect

另请参阅

增强现有类型

TypeDecorator.process_result_value()

method process_literal_param(value: _T | None, dialect: Dialect) → str

接收要在语句中内联呈现的文字参数值。

注意

这个方法在SQL 编译阶段的语句执行时被调用,用于渲染 SQL 字符串。与其他 SQL 编译方法不同,它接收一个特定的 Python 值作为字符串进行渲染。但是不要将其与TypeDecorator.process_bind_param()方法混淆,后者是在语句执行时处理传递给特定参数的实际值的更典型的方法。

TypeDecorator的自定义子类应重写此方法,以提供对特殊情况下作为文字呈现的传入数据值的自定义行为。

返回的字符串将被渲染到输出字符串中。

method process_result_value(value: Any | None, dialect: Dialect) → _T | None

接收要转换的结果行列值。

TypeDecorator的自定义子类应重写此方法,以提供从数据库结果行中接收到的数据值的自定义行为。此方法在结果提取时被调用,并传递从数据库结果行中提取的字面 Python 数据值。

操作可以是任何希望执行自定义行为的内容,例如转换或反序列化数据。

参数:

  • value – 要操作的数据,其类型由该子类中的此方法期望的类型决定。可以是None
  • dialect – 使用的Dialect

另请参阅

增强现有类型

TypeDecorator.process_bind_param()

method result_processor(dialect: Dialect, coltype: Any) → _ResultProcessorType[_T] | None

为给定的Dialect提供结果值处理函数。

这是满足TypeEngine约定的方法,用于绑定值转换,通常通过TypeEngine.result_processor()方法进行。

注意

用户定义的 TypeDecorator 的子类不应实现这个方法,而应该实现 TypeDecorator.process_result_value(),以便保持实现类型提供的“内部”处理。

参数:

  • dialect – 正在使用的方言实例。
  • coltype – 一个 SQLAlchemy 数据类型。
attribute sort_key_function: Callable[[Any], Any] | None

一个可以作为 sorted 的键传递的排序函数。

None 的默认值表示此类型存储的值是自排序的。

版本 1.3.8 中的新功能。

method type_engine(dialect: Dialect) → TypeEngine[Any]

为这个 TypeDecorator 返回一个特定方言的 TypeEngine 实例。

在大多数情况下,这将返回一个由 self.impl 表示的 TypeEngine 类型的方言适配形式。使用 dialect_impl()。通过覆盖 load_dialect_impl() 可在此处自定义行为。

TypeDecorator 配方

以下是一些关键的 TypeDecorator 配方。

将编码字符串强制转换为 Unicode

关于 Unicode 类型的一个常见困惑是,它仅用于处理 Python 端的 unicode 对象,这意味着作为绑定参数传递给它的值必须是 u'some string' 的形式,如果使用的是 Python 2 而不是 3。它执行的编码/解码函数仅适应所使用的 DBAPI 需要的内容,并且主要是一个私有实现细节。

可以通过使用需要时强制转换的 TypeDecorator 来实现安全接收 Python 字节串的用例:

from sqlalchemy.types import TypeDecorator, Unicode
class CoerceUTF8(TypeDecorator):
  """Safely coerce Python bytestrings to Unicode
 before passing off to the database."""
    impl = Unicode
    def process_bind_param(self, value, dialect):
        if isinstance(value, str):
            value = value.decode("utf-8")
        return value

四舍五入数值

一些数据库连接器(如 SQL Server 的连接器)如果传递带有太多小数位的 Decimal 会出错。以下是一个将其四舍五入的配方:

from sqlalchemy.types import TypeDecorator, Numeric
from decimal import Decimal
class SafeNumeric(TypeDecorator):
  """Adds quantization to Numeric."""
    impl = Numeric
    def __init__(self, *arg, **kw):
        TypeDecorator.__init__(self, *arg, **kw)
        self.quantize_int = -self.impl.scale
        self.quantize = Decimal(10) ** self.quantize_int
    def process_bind_param(self, value, dialect):
        if isinstance(value, Decimal) and value.as_tuple()[2] < self.quantize_int:
            value = value.quantize(self.quantize)
        return value

将时区感知时间戳存储为时区无关的 UTC 时间

数据库中的时间戳应始终以不考虑时区的方式存储。对于大多数数据库,这意味着首先将时间戳设置为 UTC  时区,然后将其存储为无时区(即,没有与之关联的任何时区;假定 UTC 为“隐式”时区)。或者,通常更喜欢使用数据库特定类型,如  PostgreSQL 的“带时区的时间戳”,因为它们具有更丰富的功能;但是,以纯 UTC  存储将在所有数据库和驱动程序上运行。当智能时区的数据库类型不可用或不受欢迎时,可以使用 TypeDecorator 创建一种将时区感知时间戳转换为时区不敏感时间戳的数据类型。下面,使用 Python 内置的 datetime.timezone.utc 时区来归一化和反归一化:

import datetime
class TZDateTime(TypeDecorator):
    impl = DateTime
    cache_ok = True
    def process_bind_param(self, value, dialect):
        if value is not None:
            if not value.tzinfo or value.tzinfo.utcoffset(value) is None:
                raise TypeError("tzinfo is required")
            value = value.astimezone(datetime.timezone.utc).replace(tzinfo=None)
        return value
    def process_result_value(self, value, dialect):
        if value is not None:
            value = value.replace(tzinfo=datetime.timezone.utc)
        return value

与后端无关的 GUID 类型

注意

自 2.0 版本起,应优先使用内置的 Uuid 类型,其行为类似。此示例仅作为接收和返回 Python 对象的类型装饰器的示例。

接收并返回 Python uuid() 对象。在使用 PostgreSQL 时使用 PG UUID 类型,在使用 MSSQL 时使用 UNIQUEIDENTIFIER,在其他后端上使用 CHAR(32),以字符串格式存储它们。GUIDHyphens 版本使用带连字符的值而不仅仅是十六进制字符串,使用 CHAR(36) 类型存储:

from operator import attrgetter
from sqlalchemy.types import TypeDecorator, CHAR
from sqlalchemy.dialects.mssql import UNIQUEIDENTIFIER
from sqlalchemy.dialects.postgresql import UUID
import uuid
class GUID(TypeDecorator):
  """Platform-independent GUID type.
 Uses PostgreSQL's UUID type or MSSQL's UNIQUEIDENTIFIER,
 otherwise uses CHAR(32), storing as stringified hex values.
 """
    impl = CHAR
    cache_ok = True
    _default_type = CHAR(32)
    _uuid_as_str = attrgetter("hex")
    def load_dialect_impl(self, dialect):
        if dialect.name == "postgresql":
            return dialect.type_descriptor(UUID())
        elif dialect.name == "mssql":
            return dialect.type_descriptor(UNIQUEIDENTIFIER())
        else:
            return dialect.type_descriptor(self._default_type)
    def process_bind_param(self, value, dialect):
        if value is None or dialect.name in ("postgresql", "mssql"):
            return value
        else:
            if not isinstance(value, uuid.UUID):
                value = uuid.UUID(value)
            return self._uuid_as_str(value)
    def process_result_value(self, value, dialect):
        if value is None:
            return value
        else:
            if not isinstance(value, uuid.UUID):
                value = uuid.UUID(value)
            return value
class GUIDHyphens(GUID):
  """Platform-independent GUID type.
 Uses PostgreSQL's UUID type or MSSQL's UNIQUEIDENTIFIER,
 otherwise uses CHAR(36), storing as stringified uuid values.
 """
    _default_type = CHAR(36)
    _uuid_as_str = str
将 Python uuid.UUID 链接到 ORM 映射的自定义类型

在使用 注释式声明表 映射声明 ORM 映射时,可以通过将其添加到 类型注解映射 中,将上述自定义 GUID 类型与 Python uuid.UUID 数据类型相关联,该类型通常定义在 DeclarativeBase 类上:

import uuid
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
    type_annotation_map = {
        uuid.UUID: GUID,
    }

通过上述配置,继承自 Base 的 ORM 映射类可以在注解中引用 Python uuid.UUID,这将自动使用 GUID

class MyModel(Base):
    __tablename__ = "my_table"
    id: Mapped[uuid.UUID] = mapped_column(primary_key=True)

另请参见

自定义类型映射

编组 JSON 字符串

此类型使用 simplejson 将 Python 数据结构编组为 JSON。可修改为使用 Python 内置的 json 编码器:

from sqlalchemy.types import TypeDecorator, VARCHAR
import json
class JSONEncodedDict(TypeDecorator):
  """Represents an immutable structure as a json-encoded string.
 Usage:
 JSONEncodedDict(255)
 """
    impl = VARCHAR
    cache_ok = True
    def process_bind_param(self, value, dialect):
        if value is not None:
            value = json.dumps(value)
        return value
    def process_result_value(self, value, dialect):
        if value is not None:
            value = json.loads(value)
        return value
添加可变性

默认情况下,ORM 不会检测上述类型的“可变性”——这意味着,对值的原地更改不会被检测到,也不会被刷新。如果没有进一步的步骤,您将需要在每个父对象上使用新对象替换现有值以检测更改:

obj.json_value["key"] = "value"  # will *not* be detected by the ORM
obj.json_value = {"key": "value"}  # *will* be detected by the ORM

上述限制可能是可以接受的,因为许多应用程序可能不需要在创建后对值进行任何变异。对于那些确实具有此要求的应用程序,最好使用sqlalchemy.ext.mutable扩展来支持可变性。对于以字典为导向的 JSON 结构,我们可以这样应用:

json_type = MutableDict.as_mutable(JSONEncodedDict)
class MyClass(Base):
    #  ...
    json_data = Column(json_type)

另请参阅

变异跟踪

处理比较操作

TypeDecorator的默认行为是将任何表达式的“右侧”强制转换为相同类型。对于像 JSON  这样的类型,这意味着任何使用的操作符必须在 JSON 方面有意义。对于某些情况,用户可能希望在某些情况下使类型像 JSON  一样行为,在其他情况下像纯文本一样行为。一个例子是如果想要处理 JSON 类型的 LIKE 操作符。LIKE 对 JSON  结构没有意义,但对底层文本表示有意义。要使用JSONEncodedDict这样的类型来实现这一点,我们需要在尝试使用此操作符之前使用cast()type_coerce()将列强制转换为文本形式:

from sqlalchemy import type_coerce, String
stmt = select(my_table).where(type_coerce(my_table.c.json_data, String).like("%foo%"))

TypeDecorator提供了一个内置系统,用于基于操作符构建这些类型转换。如果我们想要经常使用 LIKE 操作符,并将我们的 JSON 对象解释为字符串,我们可以通过覆盖TypeDecorator.coerce_compared_value()方法将其构建到类型中:

from sqlalchemy.sql import operators
from sqlalchemy import String
class JSONEncodedDict(TypeDecorator):
    impl = VARCHAR
    cache_ok = True
    def coerce_compared_value(self, op, value):
        if op in (operators.like_op, operators.not_like_op):
            return String()
        else:
            return self
    def process_bind_param(self, value, dialect):
        if value is not None:
            value = json.dumps(value)
        return value
    def process_result_value(self, value, dialect):
        if value is not None:
            value = json.loads(value)
        return value

以上只是处理像“LIKE”这样的操作符的一种方法。其他应用程序可能希望对于 JSON 对象没有意义的操作符(如“LIKE”)引发NotImplementedError,而不是自动强制转换为文本。


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

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