SqlAlchemy-2-0-中文文档-二十五-

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,而不是自动强制转换为文本。

应用 SQL 级别的绑定/结果处理

如在扩展现有类型一节中所见,SQLAlchemy 允许在参数发送到语句时以及从数据库加载结果行时调用 Python 函数,以对发送到或从数据库的值应用转换。还可以定义 SQL 级别的转换。其理念在于,当只有关系数据库包含一系列必要的函数来在应用程序和持久性格式之间强制转换传入和传出数据时。示例包括使用数据库定义的加密/解密函数,以及处理地理数据的存储过程。

任何 TypeEngineUserDefinedTypeTypeDecorator 的子类都可以包含 TypeEngine.bind_expression() 和/或 TypeEngine.column_expression() 的实现,当定义为返回非 None 值时,应返回一个 ColumnElement 表达式,以注入到 SQL 语句中,要么围绕绑定参数,要么是列表达式。例如,要构建一个 Geometry 类型,它将对所有传出值应用 PostGIS 函数 ST_GeomFromText,并对所有传入数据应用函数 ST_AsText,我们可以创建我们自己的 UserDefinedType 的子类,并与 func 一起提供这些方法:

from sqlalchemy import func
from sqlalchemy.types import UserDefinedType

class Geometry(UserDefinedType):
    def get_col_spec(self):
        return "GEOMETRY"

    def bind_expression(self, bindvalue):
        return func.ST_GeomFromText(bindvalue, type_=self)

    def column_expression(self, col):
        return func.ST_AsText(col, type_=self)

我们可以将 Geometry 类型应用到 Table 元数据中,并在 select() 构造中使用它:

geometry = Table(
    "geometry",
    metadata,
    Column("geom_id", Integer, primary_key=True),
    Column("geom_data", Geometry),
)

print(
    select(geometry).where(
        geometry.c.geom_data == "LINESTRING(189412 252431,189631 259122)"
    )
)

结果的 SQL 将两个函数嵌入到适当的位置。ST_AsText 应用于列子句,以便返回值在进入结果集之前通过该函数运行,而 ST_GeomFromText 则在绑定参数上运行,以便转换传入的值:

SELECT  geometry.geom_id,  ST_AsText(geometry.geom_data)  AS  geom_data_1
FROM  geometry
WHERE  geometry.geom_data  =  ST_GeomFromText(:geom_data_2)

TypeEngine.column_expression() 方法与编译器的机制交互,以确保 SQL 表达式不会干扰包装表达式的标记。例如,如果我们对表达式的 label() 渲染一个 select(),字符串标签将移动到包装表达式的外部:

print(select(geometry.c.geom_data.label("my_data")))

输出:

SELECT  ST_AsText(geometry.geom_data)  AS  my_data
FROM  geometry

另一个例子是我们装饰 BYTEA 以提供一个 PGPString,它将使用 PostgreSQL 的 pgcrypto 扩展来透明地加密/解密值:

from sqlalchemy import (
    create_engine,
    String,
    select,
    func,
    MetaData,
    Table,
    Column,
    type_coerce,
    TypeDecorator,
)

from sqlalchemy.dialects.postgresql import BYTEA

class PGPString(TypeDecorator):
    impl = BYTEA

    cache_ok = True

    def __init__(self, passphrase):
        super(PGPString, self).__init__()

        self.passphrase = passphrase

    def bind_expression(self, bindvalue):
        # convert the bind's type from PGPString to
        # String, so that it's passed to psycopg2 as is without
        # a dbapi.Binary wrapper
        bindvalue = type_coerce(bindvalue, String)
        return func.pgp_sym_encrypt(bindvalue, self.passphrase)

    def column_expression(self, col):
        return func.pgp_sym_decrypt(col, self.passphrase)

metadata_obj = MetaData()
message = Table(
    "message",
    metadata_obj,
    Column("username", String(50)),
    Column("message", PGPString("this is my passphrase")),
)

engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test", echo=True)
with engine.begin() as conn:
    metadata_obj.create_all(conn)

    conn.execute(
        message.insert(),
        {"username": "some user", "message": "this is my message"},
    )

    print(
        conn.scalar(select(message.c.message).where(message.c.username == "some user"))
    )

pgp_sym_encryptpgp_sym_decrypt 函数应用于 INSERT 和 SELECT 语句:

INSERT  INTO  message  (username,  message)
  VALUES  (%(username)s,  pgp_sym_encrypt(%(message)s,  %(pgp_sym_encrypt_1)s))
  -- {'username': 'some user', 'message': 'this is my message',
  --  'pgp_sym_encrypt_1': 'this is my passphrase'}

SELECT  pgp_sym_decrypt(message.message,  %(pgp_sym_decrypt_1)s)  AS  message_1
  FROM  message
  WHERE  message.username  =  %(username_1)s
  -- {'pgp_sym_decrypt_1': 'this is my passphrase', 'username_1': 'some user'}
```  ## 重新定义和创建新操作符

SQLAlchemy Core 定义了一组固定的表达式运算符,可用于所有列表达式。其中一些操作的效果是重载 Python 的内置运算符;此类运算符的示例包括`ColumnOperators.__eq__()`(`table.c.somecolumn == 'foo'`)、`ColumnOperators.__invert__()`(`~table.c.flag`)和`ColumnOperators.__add__()`(`table.c.x + table.c.y`)。其他运算符作为列表达式的显式方法公开,例如`ColumnOperators.in_()`(`table.c.value.in_(['x', 'y'])`)和`ColumnOperators.like()`(`table.c.value.like('%ed%')`)。

当需要使用尚未直接支持的 SQL 运算符时,最快的方法是在任何 SQL 表达式对象上使用`Operators.op()`方法;此方法接受表示要渲染的 SQL 运算符的字符串,并返回一个 Python 可调用对象,该对象接受任意的右侧表达式:

```py
>>> from sqlalchemy import column
>>> expr = column("x").op(">>")(column("y"))
>>> print(expr)
x  >>  y 

当使用自定义 SQL 类型时,还有一种方法可以实现自定义运算符,这些运算符与使用该列类型的任何列表达式自动关联,而无需每次使用运算符时直接调用Operators.op()

为了实现这一点,SQL 表达式构造会查询与构造关联的TypeEngine对象,以确定内置运算符的行为,以及查找可能已调用的新方法。 TypeEngine 定义了一个由Comparator类实现的“比较”对象,为 SQL 运算符提供基本行为,许多特定类型提供了该类的自己的子实现。用户定义的Comparator实现可以直接构建到特定类型的简单子类中,以覆盖或定义新操作。下面,我们创建一个Integer子类,该子类重写了ColumnOperators.__add__()运算符,而该运算符又使用Operators.op()来生成自定义的 SQL 本身:

from sqlalchemy import Integer

class MyInt(Integer):
    class comparator_factory(Integer.Comparator):
        def __add__(self, other):
            return self.op("goofy")(other)

上述配置创建了一个名为MyInt的新类,该类将TypeEngine.comparator_factory属性设置为指向一个新的类,该类是Integer类型相关联的Comparator类的子类。

用法:

>>> sometable = Table("sometable", metadata, Column("data", MyInt))
>>> print(sometable.c.data + 5)
sometable.data  goofy  :data_1 

ColumnOperators.__add__()的实现由拥有的 SQL 表达式查询,通过将其自身作为expr属性来实例化Comparator。当实现需要直接引用源 ColumnElement 对象时,可以使用该属性:

from sqlalchemy import Integer

class MyInt(Integer):
    class comparator_factory(Integer.Comparator):
        def __add__(self, other):
            return func.special_addition(self.expr, other)

添加到 Comparator 的新方法通过动态查找方案暴露在拥有的 SQL 表达式对象上,该方案将 Comparator 添加到拥有的 ColumnElement 表达式构造上的方法。例如,要为整数添加 log() 函数:

from sqlalchemy import Integer, func

class MyInt(Integer):
    class comparator_factory(Integer.Comparator):
        def log(self, other):
            return func.log(self.expr, other)

使用上述类型:

>>> print(sometable.c.data.log(5))
log(:log_1,  :log_2) 

当使用返回布尔结果的比较操作的 Operators.op() 时,应将 Operators.op.is_comparison 标志设置为 True

class MyInt(Integer):
    class comparator_factory(Integer.Comparator):
        def is_frobnozzled(self, other):
            return self.op("--is_frobnozzled->", is_comparison=True)(other)

一元操作也是可能的。例如,要添加 PostgreSQL 阶乘运算符的实现,我们将 UnaryExpression 构造与 custom_op 结合起来产生阶乘表达式:

from sqlalchemy import Integer
from sqlalchemy.sql.expression import UnaryExpression
from sqlalchemy.sql import operators

class MyInteger(Integer):
    class comparator_factory(Integer.Comparator):
        def factorial(self):
            return UnaryExpression(
                self.expr, modifier=operators.custom_op("!"), type_=MyInteger
            )

使用上述类型:

>>> from sqlalchemy.sql import column
>>> print(column("x", MyInteger).factorial())
x  ! 

另请参阅

Operators.op()

TypeEngine.comparator_factory

创建新类型

UserDefinedType 类被提供为定义全新数据库类型的简单基类。使用此类来表示 SQLAlchemy 不知道的原生数据库类型。如果只需要 Python 转换行为,请改用 TypeDecorator

对象名称 描述
UserDefinedType 用户自定义类型的基础。
class sqlalchemy.types.UserDefinedType

用户自定义类型的基础。

这应该是新类型的基础。注意,对于大多数情况,TypeDecorator 可能更合适:

import sqlalchemy.types as types

class MyType(types.UserDefinedType):
    cache_ok = True

    def __init__(self, precision = 8):
        self.precision = precision

    def get_col_spec(self, **kw):
        return "MYTYPE(%s)" % self.precision

    def bind_processor(self, dialect):
        def process(value):
            return value
        return process

    def result_processor(self, dialect, coltype):
        def process(value):
            return value
        return process

一旦类型创建完成,就可以立即使用:

table = Table('foo', metadata_obj,
    Column('id', Integer, primary_key=True),
    Column('data', MyType(16))
    )

get_col_spec() 方法在大多数情况下将接收一个关键字参数 type_expression,该参数指的是正在编译的类型的拥有表达式,例如 Columncast() 构造。只有在方法接受关键字参数(例如 **kw)时才会发送此关键字;对于支持此函数的旧形式,使用内省来检查是否存在此关键字。

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

新版本 1.4.28 中的更新:将 ExternalType.cache_ok 标志泛化,以便它对 TypeDecorator 和 'dUserDefinedType`](#sqlalchemy.types.UserDefinedType "sqlalchemy.types.UserDefinedType") 都可用。

成员

cache_ok, coerce_compared_value(), ensure_kwarg

类签名

sqlalchemy.types.UserDefinedTypesqlalchemy.types.ExternalTypesqlalchemy.types.TypeEngineMixinsqlalchemy.types.TypeEnginesqlalchemy.util.langhelpers.EnsureKWArg)

attribute cache_ok: bool | None = None

继承自 ExternalType.cache_ok 属性的 ExternalType

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

默认值 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 中新增:- 添加了 cache_ok 标志,以允许对 TypeDecorator 类进行某些缓存的可配置性。

新版本 1.4.28 中新增:- 添加了ExternalType mixin,该 mixin 将 cache_ok 标志泛化到了 TypeDecoratorUserDefinedType 类。

另请参见

SQL 编译缓存

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

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

UserDefinedType的默认行为与TypeDecorator的默认行为相同;默认情况下,它返回self,假设比较的值应强制转换为与此相同类型。有关更多详细信息,请参阅TypeDecorator.coerce_compared_value()

attribute ensure_kwarg: str = 'get_col_spec'

表示应接受**kw参数的方法名称的正则表达式。

类将扫描与名称模板匹配的方法,并在必要时装饰它们,以确保接受**kw参数。

使用自定义类型和反射

需要注意的是,被修改以具有附加 Python 行为的数据库类型,包括基于TypeDecorator的类型以及其他用户定义的数据类型子类,不在数据库模式中具有任何表示。当使用数据库中所述的反射功能时 Reflecting Database Objects,SQLAlchemy 使用一个固定映射,将数据库服务器报告的数据类型信息链接到 SQLAlchemy 数据类型对象。例如,如果我们在 PostgreSQL 模式中查看特定数据库列的定义,我们可能会收到字符串"VARCHAR"。SQLAlchemy 的 PostgreSQL 方言具有一个硬编码映射,将字符串名称"VARCHAR"链接到 SQLAlchemy VARCHAR类,这就是为什么当我们发出类似Table('my_table', m, autoload_with=engine)的语句时,其中的Column对象将在其内部具有VARCHAR的实例的原因。

这意味着如果一个Table对象使用的类型对象与数据库本地类型名称不直接对应,如果我们在其他地方使用反射为此数据库表创建一个新的Table对象,并针对一个新的MetaData集合,那么它将不会有此数据类型。例如:

>>> from sqlalchemy import (
...     Table,
...     Column,
...     MetaData,
...     create_engine,
...     PickleType,
...     Integer,
... )
>>> metadata = MetaData()
>>> my_table = Table(
...     "my_table", metadata, Column("id", Integer), Column("data", PickleType)
... )
>>> engine = create_engine("sqlite://", echo="debug")
>>> my_table.create(engine)
INFO  sqlalchemy.engine.base.Engine
CREATE  TABLE  my_table  (
  id  INTEGER,
  data  BLOB
) 

在上面,我们使用了PickleType,这是一个在LargeBinary数据类型上工作的TypeDecorator,在 SQLite 中对应于数据库类型BLOB。在 CREATE TABLE 中,我们看到使用了BLOB数据类型。SQLite 数据库对我们使用的PickleType一无所知。

如果我们查看my_table.c.data.type的数据类型,因为这是我们直接创建的一个 Python 对象,它是PickleType

>>> my_table.c.data.type
PickleType()

然而,如果我们使用反射创建另一个Table实例,我们创建的 SQLite 数据库中不会表示使用了PickleType;相反,我们会得到BLOB

>>> metadata_two = MetaData()
>>> my_reflected_table = Table("my_table", metadata_two, autoload_with=engine)
INFO  sqlalchemy.engine.base.Engine  PRAGMA  main.table_info("my_table")
INFO  sqlalchemy.engine.base.Engine  ()
DEBUG  sqlalchemy.engine.base.Engine  Col  ('cid',  'name',  'type',  'notnull',  'dflt_value',  'pk')
DEBUG  sqlalchemy.engine.base.Engine  Row  (0,  'id',  'INTEGER',  0,  None,  0)
DEBUG  sqlalchemy.engine.base.Engine  Row  (1,  'data',  'BLOB',  0,  None,  0)

>>>  my_reflected_table.c.data.type
BLOB() 

通常,当应用程序定义具有自定义类型的显式Table元数据时,没有必要使用表反射,因为必要的Table元数据已经存在。但是,对于需要同时使用显式Table元数据(其中包括自定义的 Python 级别数据类型)以及设置其Column对象作为从数据库反映出的Table对象的应用程序或它们的组合的情况,仍然需要展示自定义数据类型的附加 Python 行为,必须采取额外的步骤来允许这样做。

最简单的方法是根据重写反射列中描述的重写特定列。在这种技术中,我们简单地使用反射与那些我们想要使用自定义或装饰的数据类型的列的显式Column对象结合使用:

>>> metadata_three = MetaData()
>>> my_reflected_table = Table(
...     "my_table",
...     metadata_three,
...     Column("data", PickleType),
...     autoload_with=engine,
... )

上面的my_reflected_table对象被反射,并将从 SQLite 数据库加载“id”列的定义。但对于“data”列,我们已经使用了一个显式的Column定义来覆盖反射对象,其中包括我们想要的 Python 数据类型,PickleType。反射过程将保持此Column对象不变:

>>> my_reflected_table.c.data.type
PickleType()

一个更加详细的将数据库原生类型对象转换为自定义数据类型的方法是使用DDLEvents.column_reflect()事件处理程序。例如,如果我们知道我们想要所有的BLOB数据类型实际上是PickleType,我们可以设置一个全局规则:

from sqlalchemy import BLOB
from sqlalchemy import event
from sqlalchemy import PickleType
from sqlalchemy import Table

@event.listens_for(Table, "column_reflect")
def _setup_pickletype(inspector, table, column_info):
    if isinstance(column_info["type"], BLOB):
        column_info["type"] = PickleType()

当上述代码在任何表反射发生之前被调用之前(还要注意它应该仅在应用程序中调用一次,因为它是一个全局规则)时,当反射包含有BLOB数据类型的任何Table时,结果数据类型将存储在Column对象中,作为PickleType

在实践中,上述基于事件的方法可能会有额外的规则,以便只影响那些数据类型重要的列,比如表名和可能列名的查找表,或者其他启发式方法,以准确确定应该用 Python 数据类型来建立哪些列。

重写类型编译

经常需要强制使用类型的“字符串”版本,即在 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 编译器尝试为使用此类型的语句生成缓存键时,将最初生成警告。如果不能保证每次生成的绑定/结果行为和 SQL 生成都相同,则应将此标志设置为 False;否则,如果该类每次都产生相同的行为,则可以将其设置为 True。有关此功能的更多说明,请参阅 TypeDecorator.cache_ok

接收 Python 类型而不是与最终使用的类型相似的 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的语句是否“可以缓存”。

默认值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 版本中新增:- 添加了cache_ok标志,以允许对TypeDecorator类的缓存进行一些可配置性。

1.4.28 版本中新增:- 添加了ExternalType mixin,将cache_ok标志泛化到TypeDecoratorUserDefinedType类中。

另请参阅

SQL 编译缓存

class Comparator

一个专门针对TypeDecoratorComparator

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

类签名

sqlalchemy.types.TypeDecorator.Comparator (sqlalchemy.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() 方法在 SQL 编译 阶段的语句中发生。

注意

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

参数:

dialect – 正在使用的 Dialect 实例。

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

为表达式中的‘强制’Python 值建议一种类型。

默认情况下,返回 self。 当此类型的对象位于尚未分配 SQLAlchemy 类型的纯 Python 对象的表达式的左侧或右侧时,表达式系统会调用此方法:

expr = table.c.somecolumn + 35

在上述情况中,如果somecolumn使用了这种类型,则将使用值operator.add35调用此方法。 返回值是应该用于此特定操作的35的任何 SQLAlchemy 类型。

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

指定那些应该在表达式级别上被强制转换为“IS ”的 Python 类型,当使用==进行比较时(与!=结合使用时同样适用于IS NOT)。

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

TypeDecorator修改此列表以仅包括NoneType,因为处理布尔类型的类型装饰器实现很常见。

自定义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对象。

这是一个最终用户覆盖的钩子,可用于根据给定的方言提供不同的类型。它由TypeDecoratortype_engine()实现使用,以帮助确定对于给定的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 一侧处理 Python unicode 对象,这意味着作为绑定参数传递给它的值必须是 u'some string' 的形式,如果使用的是 Python 2 而不是 3。它执行的编码/解码函数仅适合于所使用的 DBAPI 所需,并且主要是一个私有的实现细节。

通过使用 TypeDecorator ,可以实现安全接收 Python 字节串的类型的用例,即包含非 ASCII 字符并且在 Python 2 中不是 u'' 对象的字符串,必要时进行强制转换:

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 的 “TIMESTAMP WITH TIMEZONE” ,因为其功能更丰富;但是,存储为纯 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,而不是自动强制转换为文本。

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

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

可以使用TypeDecorator 实现根据需要进行转换的类型的使用案例,该类型可以安全地接收 Python 字节串,即包含非 ASCII 字符并且不是 Python 2 中的u''对象的字符串:

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

四舍五入数值

如果传递的 Decimal 具有太多小数位数,则某些数据库连接器(例如 SQL Server 的连接器)会中断。下面是一个将其舍入的方法:

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)

另请参阅

自定义类型映射

将 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,而不是自动强制转换为文本。

添加可变性

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 运算符。对于 JSON 结构来说,LIKE 没有意义,但对于基础文本表示来说是有意义的。要想在像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,而不是自动转换为文本。

应用 SQL 级别的绑定/结果处理

如在扩展现有类型部分所示,SQLAlchemy 允许在向语句发送参数以及从数据库加载结果行时调用 Python 函数,以对值进行转换,使其在发送到数据库时或从数据库加载时进行转换。还可以定义 SQL 级别的转换。这里的理念是,当只有关系数据库包含特定系列的函数时,这些函数对于在应用程序和持久性格式之间转换传入和传出数据是必要的。示例包括使用数据库定义的加密/解密函数,以及处理地理数据的存储过程。

任何TypeEngineUserDefinedTypeTypeDecorator子类都可以包含TypeEngine.bind_expression()和/或TypeEngine.column_expression()的实现,当定义为返回非None值时,应返回一个要注入到 SQL 语句中的ColumnElement表达式,无论是围绕绑定参数还是列表达式。例如,为了构建一个Geometry类型,该类型将对所有传出值应用 PostGIS 函数ST_GeomFromText,对所有传入数据应用函数ST_AsText,我们可以创建自己的UserDefinedType子类,该子类提供这些方法与func一起使用:

from sqlalchemy import func
from sqlalchemy.types import UserDefinedType

class Geometry(UserDefinedType):
    def get_col_spec(self):
        return "GEOMETRY"

    def bind_expression(self, bindvalue):
        return func.ST_GeomFromText(bindvalue, type_=self)

    def column_expression(self, col):
        return func.ST_AsText(col, type_=self)

我们可以将Geometry类型应用到Table元数据中,并在select()构造中使用它:

geometry = Table(
    "geometry",
    metadata,
    Column("geom_id", Integer, primary_key=True),
    Column("geom_data", Geometry),
)

print(
    select(geometry).where(
        geometry.c.geom_data == "LINESTRING(189412 252431,189631 259122)"
    )
)

结果的 SQL 嵌入了两个函数。ST_AsText应用于列子句,以便返回值在传递到结果集之前通过函数运行,而ST_GeomFromText应用于绑定参数,以便传入值被转换:

SELECT  geometry.geom_id,  ST_AsText(geometry.geom_data)  AS  geom_data_1
FROM  geometry
WHERE  geometry.geom_data  =  ST_GeomFromText(:geom_data_2)

TypeEngine.column_expression()方法与编译器的机制交互,使得 SQL 表达式不会干扰包装表达式的标记。例如,如果我们针对我们表达式的label()进行了select(),字符串标签会移动到包装表达式的外部:

print(select(geometry.c.geom_data.label("my_data")))

输出:

SELECT  ST_AsText(geometry.geom_data)  AS  my_data
FROM  geometry

另一个例子是我们装饰BYTEA以提供PGPString,这将利用 PostgreSQL 的pgcrypto扩展来透明地加密/解密值:

from sqlalchemy import (
    create_engine,
    String,
    select,
    func,
    MetaData,
    Table,
    Column,
    type_coerce,
    TypeDecorator,
)

from sqlalchemy.dialects.postgresql import BYTEA

class PGPString(TypeDecorator):
    impl = BYTEA

    cache_ok = True

    def __init__(self, passphrase):
        super(PGPString, self).__init__()

        self.passphrase = passphrase

    def bind_expression(self, bindvalue):
        # convert the bind's type from PGPString to
        # String, so that it's passed to psycopg2 as is without
        # a dbapi.Binary wrapper
        bindvalue = type_coerce(bindvalue, String)
        return func.pgp_sym_encrypt(bindvalue, self.passphrase)

    def column_expression(self, col):
        return func.pgp_sym_decrypt(col, self.passphrase)

metadata_obj = MetaData()
message = Table(
    "message",
    metadata_obj,
    Column("username", String(50)),
    Column("message", PGPString("this is my passphrase")),
)

engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test", echo=True)
with engine.begin() as conn:
    metadata_obj.create_all(conn)

    conn.execute(
        message.insert(),
        {"username": "some user", "message": "this is my message"},
    )

    print(
        conn.scalar(select(message.c.message).where(message.c.username == "some user"))
    )

pgp_sym_encryptpgp_sym_decrypt函数应用于 INSERT 和 SELECT 语句:

INSERT  INTO  message  (username,  message)
  VALUES  (%(username)s,  pgp_sym_encrypt(%(message)s,  %(pgp_sym_encrypt_1)s))
  -- {'username': 'some user', 'message': 'this is my message',
  --  'pgp_sym_encrypt_1': 'this is my passphrase'}

SELECT  pgp_sym_decrypt(message.message,  %(pgp_sym_decrypt_1)s)  AS  message_1
  FROM  message
  WHERE  message.username  =  %(username_1)s
  -- {'pgp_sym_decrypt_1': 'this is my passphrase', 'username_1': 'some user'}

重新定义和创建新操作符

SQLAlchemy 核心定义了一组固定的表达式操作符,可用于所有列表达式。其中一些操作具有重载 Python 内置操作符的效果;此类操作符的示例包括ColumnOperators.__eq__()table.c.somecolumn == 'foo')、ColumnOperators.__invert__()~table.c.flag)和ColumnOperators.__add__()table.c.x + table.c.y)。其他操作符作为列表达式上的显式方法公开,例如ColumnOperators.in_()table.c.value.in_(['x', 'y']))和ColumnOperators.like()table.c.value.like('%ed%'))。

当需要使用 SQL 操作符而已直接支持的情况下,最方便的方法是在任何 SQL 表达式对象上使用Operators.op()方法;该方法接受一个表示要呈现的 SQL 操作符的字符串,并返回一个接受任意右侧表达式的 Python 可调用对象:

>>> from sqlalchemy import column
>>> expr = column("x").op(">>")(column("y"))
>>> print(expr)
x  >>  y 

当使用自定义 SQL 类型时,还有一种实现自定义操作符的方法,就像上面提到的那样,这些操作符在使用该列类型的任何列表达式上自动存在,而无需在每次使用操作符时直接调用Operators.op()

为了实现这一点,SQL 表达式构造会参考与构造关联的TypeEngine对象,以确定内置运算符的行为,并寻找可能已被调用的新方法。TypeEngine定义了一个由Comparator类实现的“比较”对象,为 SQL 运算符提供基本行为,许多具体类型提供了它们自己的此类的子实现。用户定义的Comparator实现可以直接构建到特定类型的简单子类中,以覆盖或定义新操作。下面,我们创建一个Integer子类,它重写了ColumnOperators.__add__()运算符,而该运算符又使用Operators.op()来生成自定义的 SQL 代码:

from sqlalchemy import Integer

class MyInt(Integer):
    class comparator_factory(Integer.Comparator):
        def __add__(self, other):
            return self.op("goofy")(other)

上述配置创建了一个新的类MyInt,它将TypeEngine.comparator_factory属性设置为引用一个新的类,该类是与Integer类型关联的Comparator类的子类。

使用方法:

>>> sometable = Table("sometable", metadata, Column("data", MyInt))
>>> print(sometable.c.data + 5)
sometable.data  goofy  :data_1 

对于ColumnOperators.__add__()的实现是由拥有的 SQL 表达式进行参考的,通过使用自身作为expr属性来实例化Comparator。当实现需要直接引用原始ColumnElement对象时,可以使用此属性:

from sqlalchemy import Integer

class MyInt(Integer):
    class comparator_factory(Integer.Comparator):
        def __add__(self, other):
            return func.special_addition(self.expr, other)

对于 Comparator 添加的新方法,通过动态查找方案暴露在拥有 SQL 表达式对象上,这样可以将添加到 Comparator 的方法暴露到拥有的 ColumnElement 表达式构造上。例如,要为整数添加一个 log() 函数:

from sqlalchemy import Integer, func

class MyInt(Integer):
    class comparator_factory(Integer.Comparator):
        def log(self, other):
            return func.log(self.expr, other)

使用上述类型:

>>> print(sometable.c.data.log(5))
log(:log_1,  :log_2) 

在使用 Operators.op() 进行返回布尔结果的比较操作时,应将 Operators.op.is_comparison 标志设置为 True

class MyInt(Integer):
    class comparator_factory(Integer.Comparator):
        def is_frobnozzled(self, other):
            return self.op("--is_frobnozzled->", is_comparison=True)(other)

一元操作也是可能的。例如,要添加 PostgreSQL 阶乘运算符的实现,我们结合 UnaryExpression 构造以及 custom_op 来生成阶乘表达式:

from sqlalchemy import Integer
from sqlalchemy.sql.expression import UnaryExpression
from sqlalchemy.sql import operators

class MyInteger(Integer):
    class comparator_factory(Integer.Comparator):
        def factorial(self):
            return UnaryExpression(
                self.expr, modifier=operators.custom_op("!"), type_=MyInteger
            )

使用上述类型:

>>> from sqlalchemy.sql import column
>>> print(column("x", MyInteger).factorial())
x  ! 

另请参阅

Operators.op()

TypeEngine.comparator_factory

创建新类型

UserDefinedType 类被提供作为定义全新数据库类型的简单基类。使用它来表示 SQLAlchemy 不知道的本地数据库类型。如果只需要 Python 转换行为,请改用 TypeDecorator

对象名称 描述
UserDefinedType 用户定义类型的基础。
class sqlalchemy.types.UserDefinedType

用户定义类型的基础。

这应该是新类型的基础。请注意,对于大多数情况,TypeDecorator 可能更合适:

import sqlalchemy.types as types

class MyType(types.UserDefinedType):
    cache_ok = True

    def __init__(self, precision = 8):
        self.precision = precision

    def get_col_spec(self, **kw):
        return "MYTYPE(%s)" % self.precision

    def bind_processor(self, dialect):
        def process(value):
            return value
        return process

    def result_processor(self, dialect, coltype):
        def process(value):
            return value
        return process

一旦类型被创建,它立即可用:

table = Table('foo', metadata_obj,
    Column('id', Integer, primary_key=True),
    Column('data', MyType(16))
    )

get_col_spec() 方法在大多数情况下将接收一个关键字参数 type_expression,该参数指的是类型的拥有表达式在编译时,例如 Columncast() 构造。仅当方法接受关键字参数(例如 **kw)时才发送此关键字;用于检查此函数的传统形式的内省。

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

新版本 1.4.28 中:将 ExternalType.cache_ok 标志泛化,以便它同时适用于 TypeDecoratorUserDefinedType

成员

cache_ok,coerce_compared_value(),ensure_kwarg

类签名

sqlalchemy.types.UserDefinedTypesqlalchemy.types.ExternalTypesqlalchemy.types.TypeEngineMixinsqlalchemy.types.TypeEnginesqlalchemy.util.langhelpers.EnsureKWArg

attribute cache_ok: bool | None = None

继承自 ExternalType.cache_ok 属性的 ExternalType

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

默认值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" ...

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

>>> 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 中新增了:- 添加了cache_ok标志,允许对TypeDecorator类的缓存进行一些可配置性。

新版本 1.4.28 中新增了:- 添加了ExternalType混合类型,它将cache_ok标志推广到了TypeDecoratorUserDefinedType类。

另请参阅

SQL 编译缓存

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

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

UserDefinedType 的默认行为与 TypeDecorator 的默认行为相同;默认情况下,它返回 self,假设比较的值应该被强制转换为与此相同的类型。有关更多详细信息,请参见 TypeDecorator.coerce_compared_value()

attribute ensure_kwarg: str = 'get_col_spec'

一个用于指示方法名称的正则表达式,该方法应接受**kw参数。

类将扫描匹配名称模板的方法,并在必要时装饰它们,以确保接受**kw参数。

使用自定义类型和反射

需要注意的是,被修改以具有额外的 Python 行为的数据库类型,包括基于TypeDecorator的类型以及其他用户定义的数据类型子类,在数据库模式中没有任何表示。当使用数据库中描述的反射功能时,SQLAlchemy 使用一个固定的映射,将数据库服务器报告的数据类型信息链接到一个 SQLAlchemy 数据类型对象上。例如,如果我们在 PostgreSQL 模式中查看特定数据库列的定义,可能会收到字符串"VARCHAR"。SQLAlchemy 的 PostgreSQL 方言有一个硬编码的映射,将字符串名称"VARCHAR"链接到 SQLAlchemy VARCHAR 类,这就是当我们发出像Table('my_table', m, autoload_with=engine)这样的语句时,其中的 Column 对象内会有一个 VARCHAR 的实例存在的原因。

这意味着如果一个 Table 对象使用的类型对象不直接对应于数据库本机类型名称,如果我们在其他地方使用反射为此数据库表创建新的 Table 对象,则它将没有此数据类型。例如:

>>> from sqlalchemy import (
...     Table,
...     Column,
...     MetaData,
...     create_engine,
...     PickleType,
...     Integer,
... )
>>> metadata = MetaData()
>>> my_table = Table(
...     "my_table", metadata, Column("id", Integer), Column("data", PickleType)
... )
>>> engine = create_engine("sqlite://", echo="debug")
>>> my_table.create(engine)
INFO  sqlalchemy.engine.base.Engine
CREATE  TABLE  my_table  (
  id  INTEGER,
  data  BLOB
) 

在上面,我们使用了PickleType,它是一个作用于LargeBinary数据类型之上的TypeDecorator,在 SQLite 中对应着数据库类型BLOB。在 CREATE TABLE 中,我们可以看到使用了BLOB数据类型。SQLite 数据库对我们使用的PickleType一无所知。

如果我们看一下my_table.c.data.type的数据类型,因为这是我们直接创建的 Python 对象,它是PickleType

>>> my_table.c.data.type
PickleType()

然而,如果我们使用反射创建另一个Table实例,我们创建的 SQLite 数据库中不会反映出PickleType的使用;相反,我们得到的是BLOB:

>>> metadata_two = MetaData()
>>> my_reflected_table = Table("my_table", metadata_two, autoload_with=engine)
INFO  sqlalchemy.engine.base.Engine  PRAGMA  main.table_info("my_table")
INFO  sqlalchemy.engine.base.Engine  ()
DEBUG  sqlalchemy.engine.base.Engine  Col  ('cid',  'name',  'type',  'notnull',  'dflt_value',  'pk')
DEBUG  sqlalchemy.engine.base.Engine  Row  (0,  'id',  'INTEGER',  0,  None,  0)
DEBUG  sqlalchemy.engine.base.Engine  Row  (1,  'data',  'BLOB',  0,  None,  0)

>>>  my_reflected_table.c.data.type
BLOB() 

通常,当应用程序使用自定义类型定义明确的Table元数据时,不需要使用表反射,因为必要的Table元数据已经存在。然而,对于一个应用程序或一组应用程序需要同时使用包含自定义 Python 级数据类型的明确Table元数据以及设置其Column对象作为从数据库反映的Table对象的情况,仍然需要展示自定义数据类型的附加 Python 行为,必须采取额外的步骤来允许这种情况。

最直接的方法是按照覆盖反射列中描述的覆盖特定列。在这种技术中,我们只需将反射与那些我们想要使用自定义或装饰数据类型的列的显式Column对象结合使用:

>>> metadata_three = MetaData()
>>> my_reflected_table = Table(
...     "my_table",
...     metadata_three,
...     Column("data", PickleType),
...     autoload_with=engine,
... )

上面的my_reflected_table对象被反映出来,并将从 SQLite 数据库加载“id”列的定义。但对于“data”列,我们用一个显式的Column定义来覆盖了反射对象,其中包括我们想要的 Python 数据类型,PickleType。反射过程将保留此Column对象不变:

>>> my_reflected_table.c.data.type
PickleType()

从数据库本地类型对象转换为自定义数据类型的更详细的方法是使用DDLEvents.column_reflect()事件处理程序。例如,如果我们知道我们想要的所有BLOB数据类型实际上都是PickleType,我们可以设置一个跨越整个的规则:

from sqlalchemy import BLOB
from sqlalchemy import event
from sqlalchemy import PickleType
from sqlalchemy import Table

@event.listens_for(Table, "column_reflect")
def _setup_pickletype(inspector, table, column_info):
    if isinstance(column_info["type"], BLOB):
        column_info["type"] = PickleType()

当上述代码在任何表反射发生之前调用(还要注意它应该在应用程序中仅调用一次,因为它是一个全局规则)时,对于包含具有BLOB数据类型列的任何Table,结果数据类型将存储在Column对象中作为PickleType

实际上,上述基于事件的方法可能会有额外的规则,以便仅影响那些数据类型很重要的列,例如表名和可能列名的查找表,或者其他启发式方法,以准确确定应该用 Python 数据类型建立哪些列。

基本类型 API

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

对象名称 描述
Concatenable 标记类型支持“串联”的混合类型,通常用于字符串。
ExternalType 定义特定于第三方数据类型的属性和行为的混合类型。
Indexable 标记类型支持索引操作的混合类型,例如数组或 JSON 结构。
NullType 未知类型。
TypeEngine 所有 SQL 数据类型的最终基类。
Variant 不推荐使用。此符号用于向后兼容性,但实际上不应使用此类型。
class sqlalchemy.types.TypeEngine

所有 SQL 数据类型的最终基类。

TypeEngine 的常见子类包括 StringIntegerBoolean

有关 SQLAlchemy 类型系统的概述,请参见 SQL 数据类型对象。

另请参阅

SQL 数据类型对象

成员

operate(), reverse_operate(), adapt(), as_generic(), bind_expression(), bind_processor(), coerce_compared_value(), column_expression(), comparator_factory, compare_values(), compile(), dialect_impl(), evaluates_none(), get_dbapi_type(), hashable, literal_processor(), python_type, render_bind_cast, render_literal_cast, result_processor(), should_evaluate_none, sort_key_function, with_variant()

类签名

sqlalchemy.types.TypeEngine (sqlalchemy.sql.visitors.Visitable, typing.Generic)

class Comparator

自定义比较操作在类型级别定义的基类。请参见TypeEngine.comparator_factory

类签名

sqlalchemy.types.TypeEngine.Comparator (sqlalchemy.sql.expression.ColumnOperators, typing.Generic)

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 adapt(cls: Type[TypeEngine | TypeEngineMixin], **kw: Any) → TypeEngine

给定一个“impl”类来处理,产生此类型的“适配”形式。

此方法在内部用于将通用类型与特定于特定方言的“实现”类型关联起来。

method as_generic(allow_nulltype: bool = False) → TypeEngine

使用启发式规则返回此类型对应的通用类型的实例。如果此启发式规则不足以满足需求,则可以重写此方法。

>>> from sqlalchemy.dialects.mysql import INTEGER
>>> INTEGER(display_width=4).as_generic()
Integer()
>>> from sqlalchemy.dialects.mysql import NVARCHAR
>>> NVARCHAR(length=100).as_generic()
Unicode(length=100)

在 1.4.0b2 版本中新增。

另请参见

使用与数据库无关的类型反射 - 描述了TypeEngine.as_generic()DDLEvents.column_reflect()事件结合使用的情况,这是其预期的用法。

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

给定一个绑定值(即一个BindParameter实例),返回其位置的 SQL 表达式。

这通常是一个 SQL 函数,用于在语句中包装现有的绑定参数。它用于特殊的数据类型,这些类型需要将文本在某些特殊数据库函数中包装,以便将应用程序级值强制转换为数据库特定格式。它是TypeEngine.bind_processor()方法的 SQL 模拟。

此方法在语句的SQL 编译阶段调用,用于呈现 SQL 字符串。它针对特定值调用。

请注意,当实现此方法时,应始终返回完全相同的结构,不带任何条件逻辑,因为它可能在针对任意数量的绑定参数集的 executemany()调用中使用。

注意

此方法仅针对特定方言类型对象,通常私有于正在使用的方言,并且不是公共类型对象,这意味着无法通过子类化TypeEngine类来提供替代的TypeEngine.bind_expression()方法,除非明确地子类化UserDefinedType类。

要为TypeEngine.bind_expression()提供替代行为,请实现一个TypeDecorator类,并提供一个TypeDecorator.bind_expression()的实现。

另请参见

增强现有类型

另请参见

应用 SQL 级别的绑定/结果处理

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

返回一个转换函数以处理绑定值。

返回一个可调用对象,该对象将接收一个绑定参数值作为唯一的位置参数,并返回一个要发送到 DB-API 的值。

如果不需要处理,则该方法应返回None

注意

此方法仅针对特定方言类型对象,通常私有于正在使用的方言,并且不是公共类型对象,这意味着无法通过子类化TypeEngine类来提供替代的TypeEngine.bind_processor()方法,除非明确地子类化UserDefinedType类。

要为TypeEngine.bind_processor()提供替代行为,请实现一个TypeDecorator类,并提供一个TypeDecorator.process_bind_param()的实现。

另请参见

增强现有类型

参数:

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

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

建议为表达式中的‘强制’Python 值提供一种类型。

给定运算符和值,让类型有机会返回一个应该将值强制转换为的类型。

这里的默认行为是保守的;如果右侧已经根据其 Python 类型被强制转换为 SQL 类型,则通常会被保留。

这里的最终用户功能扩展通常应通过TypeDecorator完成,它提供更自由的行为,因为它默认将表达式的另一侧强制转换为此类型,从而应用特殊的 Python 转换,超出了 DBAPI 所需的范围。它还提供了公共方法TypeDecorator.coerce_compared_value(),用于最终用户定制此行为。

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

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

这通常是一个 SQL 函数,它将列表达式包装为在 SELECT 语句的 columns 子句中呈现的形式。它用于特殊数据类型,这些数据类型要求列在发送回应用程序之前必须被包装在某些特殊的数据库函数中以强制转换值。它是 SQL 中TypeEngine.result_processor()方法的类比。

此方法在语句的SQL 编译阶段调用,当呈现 SQL 字符串时。它不会针对特定值调用。

注意

此方法仅相对于特定方言类型对象调用,该对象通常是当前正在使用的方言的私有类型,并且不是公共类型对象,这意味着不可行通过子类化TypeEngine类来提供替代TypeEngine.column_expression()方法,除非显式子类化UserDefinedType类。

要为TypeEngine.column_expression()提供替代行为,请实现一个TypeDecorator类,并提供一个TypeDecorator.column_expression()的实现。

另见

增强现有类型

另见

应用 SQL 级别的绑定/结果处理

attribute comparator_factory

Comparator 的别名

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

比较两个值是否相等。

method compile(dialect: Dialect | None = None) → str

生成此 TypeEngine 的字符串编译形式。

当不带参数调用时,使用“默认”方言来生成字符串结果。

参数:

dialect – 一个 Dialect 实例。

method dialect_impl(dialect: Dialect) → TypeEngine[_T]

返回此 TypeEngine 的特定于方言的实现。

method evaluates_none() → Self

返回具有 should_evaluate_none 标志设置为 True 的此类型的副本。

例如:

Table(
    'some_table', metadata,
    Column(
        String(50).evaluates_none(),
        nullable=True,
        server_default='no value')
)

ORM 使用此标志来指示在 INSERT 语句中传递 None 的正值到列中,而不是省略列从 INSERT 语句中,这将触发列级默认值的效果。它还允许具有与 Python None 值关联的特殊行为的类型来指示该值不一定转换为 SQL NULL;一个典型的例子是可能希望持久化 JSON 值 'null' 的 JSON 类型。

在所有情况下,实际的 NULL SQL 值都可以通过在 INSERT 语句中使用 null SQL 构造或与 ORM 映射的属性相关联来始终持久化在任何列中。

“evaluates none” 标志 适用于传递给 Column.defaultColumn.server_defaultNone 值;在这些情况下,None 仍然表示“无默认值”。

另请参阅

强制具有默认值的列为 NULL - 在 ORM 文档中

JSON.none_as_null - 具有此标志的 PostgreSQL JSON 交互。

TypeEngine.should_evaluate_none - 类级标志

method get_dbapi_type(dbapi: module) → Any | None

返回底层 DB-API 的相应类型对象,如果有的话。

例如,这对于调用 setinputsizes() 可能很有用。

attribute hashable = True

如果 False,则表示来自此类型的值不可哈希。

在 ORM 列表结果去重时使用。

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

返回一个转换函数,用于处理直接渲染而不使用绑定的文字值。

当编译器使用“literal_binds”标志时使用此函数,通常用于 DDL 生成以及在某些后端不接受绑定参数的情况下。

返回一个可调用对象,该对象将接收一个字面的 Python 值作为唯一的位置参数,并返回一个字符串表示以在 SQL 语句中呈现。

注意

此方法仅相对于特定方言类型对象调用,该对象通常是正在使用的方言的私有对象,并不是公共类型对象,这意味着不太可能为了提供替代的TypeEngine.literal_processor()方法而对TypeEngine类进行子类化,除非显式地对UserDefinedType类进行子类化。

要为TypeEngine.literal_processor()提供替代行为,请实现一个TypeDecorator类,并提供一个TypeDecorator.process_literal_param()的实现。

另请参阅

增强现有类型

attribute python_type

返回此类型实例预计返回的 Python 类型对象,如果已知的话。

基本上,对于那些强制指定返回类型的类型,或者已知在所有常见的 DBAPI 中都会对所有类型进行这样的操作的类型(例如int),将返回该类型。

如果未定义返回类型,则引发NotImplementedError

注意,在 SQL 中,任何类型也可以容纳 NULL,这意味着你在实践中也可以从任何类型中获得None

attribute render_bind_cast = False

渲染绑定转换以适应BindTyping.RENDER_CASTS模式。

如果为 True,则此类型(通常是一个方言级别的实现类型)向编译器发出信号,表示应该在此类型的绑定参数周围呈现一个转换。

2.0 版本中的新功能。

另请参阅

BindTyping

attribute render_literal_cast = False

渲染转换当渲染一个值作为内联字面值时,例如通过TypeEngine.literal_processor()

2.0 版本中的新功能。

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

返回一个用于处理结果行值的转换函数。

返回一个可调用对象,该对象将接收一个结果行列值作为唯一的位置参数,并返回一个要返回给用户的值。

如果不需要处理,则方法应返回None

注意

此方法仅相对于特定方言类型对象调用,该对象通常是正在使用的方言私有的,并且不是与公共类型对象相同的类型对象,这意味着无法通过子类化TypeEngine类来提供替代的TypeEngine.result_processor()方法,除非明确子类化UserDefinedType类。

要为TypeEngine.result_processor()提供替代行为,请实现一个TypeDecorator类,并提供一个TypeDecorator.process_result_value()的实现。

另请参见

增强现有类型

参数:

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

  • coltype – 在cursor.description中接收到的 DBAPI coltype 参数。

attribute should_evaluate_none: bool = False

如果为 True,则 Python 常量None被认为是此类型明确处理的。

ORM 使用此标志表示在 INSERT 语句中将正值的None传递给列,而不是从 INSERT 语句中省略列,这会触发列级默认值。它还允许具有 Python None 的特殊行为的类型(例如 JSON 类型)明确指示它们希望显式处理 None 值。

要在现有类型上设置此标志,请使用TypeEngine.evaluates_none()方法。

另请参见

TypeEngine.evaluates_none()

attribute sort_key_function: Callable[[Any], Any] | None = None

作为传递给 sorted 的键的排序函数。

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

1.3.8 版中的新功能。

method with_variant(type_: _TypeEngineArgument[Any], *dialect_names: str) → Self

生成此类型对象的副本,该副本将在应用于给定名称的方言时使用给定的类型。

例如:

from sqlalchemy.types import String
from sqlalchemy.dialects import mysql

string_type = String()

string_type = string_type.with_variant(
    mysql.VARCHAR(collation='foo'), 'mysql', 'mariadb'
)

变体映射表示当特定方言解释此类型时,它将被转换为给定类型,而不是使用主类型。

从版本 2.0 开始更改:TypeEngine.with_variant()方法现在在“就地”操作TypeEngine对象时工作,返回原始类型的副本,而不是返回包装对象;不再使用Variant类。

参数:

  • type_ – 一个TypeEngine,当使用给定名称的方言时,将从原始类型中选择作为变体。

  • *dialect_names

    使用此类型的方言的一个或多个基本名称(即'postgresql''mysql'等)

    从 2.0 版本开始更改:可以为一个变体指定多个方言名称。

另请参见

使用“大写字母”和后端特定类型的多后端 - 演示了TypeEngine.with_variant()的使用。

class sqlalchemy.types.Concatenable

一个将类型标记为支持“连接”的 mixin,通常是字符串。

成员

comparator_factory

类签名

sqlalchemy.types.Concatenablesqlalchemy.types.TypeEngineMixin

class Comparator

类签名

sqlalchemy.types.Concatenable.Comparatorsqlalchemy.types.Comparator

attribute comparator_factory

Comparator的别名

class sqlalchemy.types.Indexable

一个将类型标记为支持索引操作的 mixin,例如数组或 JSON 结构。

成员

comparator_factory

类签名

sqlalchemy.types.Indexablesqlalchemy.types.TypeEngineMixin

class Comparator

类签名

sqlalchemy.types.Indexable.Comparatorsqlalchemy.types.Comparator

attribute comparator_factory

Comparator的别名

class sqlalchemy.types.NullType

一个未知类型。

NullType用作那些无法确定类型的情况的默认类型,包括:

  • 在表反射期间,当列的类型未被Dialect识别时

  • 当使用未知类型的纯 Python 对象构建 SQL 表达式时(例如somecolumn == my_special_object

  • 当创建新的Column时,并且给定类型传递为None或根本不传递时。

NullType 可以在 SQL 表达式调用中使用,没有问题,只是在表达式构造级别或绑定参数/结果处理级别上没有行为。

类签名

class sqlalchemy.types.NullType (sqlalchemy.types.TypeEngine)

class sqlalchemy.types.ExternalType

定义特定于第三方数据类型的属性和行为的混合项。

“第三方”指的是在 SQLAlchemy 范围之外定义的数据类型,在最终用户应用代码中或在 SQLAlchemy 的外部扩展中定义。

当前的子类包括 TypeDecoratorUserDefinedType

版本 1.4.28 中的新功能。

成员

cache_ok

类签名

class sqlalchemy.types.ExternalType (sqlalchemy.types.TypeEngineMixin)

attribute cache_ok: bool | None = None

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

默认值 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')

如果我们设置了这样一个缓存键,它将无法使用。我们将得到一个包含字典的元组结构,该字典本身不能作为“缓存字典”中的键使用,因为 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 中:- 添加了cache_ok标志,允许对TypeDecorator类进行一些缓存配置。

新版本 1.4.28 中:- 添加了ExternalType混合类型,将cache_ok标志泛化到TypeDecoratorUserDefinedType类。

另请参阅

SQL 编译缓存

class sqlalchemy.types.Variant

已弃用。符号用于向后兼容解决方案配方,但不应使用此实际类型。

成员

with_variant()

类签名

sqlalchemy.types.Variantsqlalchemy.types.TypeDecorator

method with_variant(type_: _TypeEngineArgument[Any], *dialect_names: str) → Self

继承自 TypeEngine.with_variant() 方法的 TypeEngine

生成将在应用于给定名称的方言时利用给定类型的此类型对象的副本。

例如:

from sqlalchemy.types import String
from sqlalchemy.dialects import mysql

string_type = String()

string_type = string_type.with_variant(
    mysql.VARCHAR(collation='foo'), 'mysql', 'mariadb'
)

变体映射表示,当特定方言解释此类型时,它将被转换为给定类型,而不是使用主要类型。

版本 2.0 中更改:TypeEngine.with_variant()方法现在与TypeEngine对象“原地”工作,返回原始类型的副本,而不是返回包装对象;不再使用Variant类。

参数:

  • type_ – 一个 TypeEngine,当使用给定名称的方言时,将从原始类型中选择作为变体的类型。

  • *dialect_names*

    使用此类型的方言的一个或多个基本名称。(即 'postgresql''mysql' 等)

    版本 2.0 中的变更:一个变体可以指定多个方言名称。

另请参阅

使用“大写”和后端特定类型进行多后端处理 - 演示了 TypeEngine.with_variant() 的使用。

引擎和连接使用

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

  • 引擎配置

    • 支持的数据库

    • 数据库 URL

      • 转义特殊字符,如密码中的@符号

      • 程序化创建 URL

      • 特定后端的 URL

    • 引擎创建 API

      • create_engine()

      • engine_from_config()

      • create_mock_engine()

      • make_url()

      • create_pool_from_url()

      • URL

    • 连接池

    • 自定义 DBAPI connect()参数 / 连接时例程

      • 传递给 dbapi.connect()的特殊关键字参数

      • 控制参数如何传递给 DBAPI connect()函数

      • 连接后修改 DBAPI 连接,或连接后运行命令

      • 完全替换 DBAPI connect()函数

    • 配置日志记录

      • 更多关于 Echo 标志的信息

      • 设置日志名称

      • 设置每个连接/子引擎令牌

      • 隐藏参数

  • 使用引擎和连接

    • 基本用法

    • 使用事务

      • 边用边提交

      • 一次性开始

      • 从引擎连接和一次性开始

      • 混合风格

    • 设置事务隔离级别,包括 DBAPI 自动提交

      • 为连接设置隔离级别或 DBAPI 自动提交

      • 为引擎设置隔离级别或 DBAPI 自动提交

      • 为单个引擎维护多个隔离级别

      • 理解 DBAPI 级别的自动提交隔离级别

    • 使用服务器端游标(即流式结果)

      • 通过 yield_per 进行固定缓冲区流式处理

      • 通过 stream_results 使用动态增长缓冲区进行流式处理

    • 模式名称的翻译

    • SQL 编译缓存

      • 配置

      • 使用日志估算缓存性能

      • 缓存使用多少内存?

      • 禁用或使用备用字典缓存部分(或全部)语句

      • 为第三方方言进行缓存

      • 使用 Lambda 函数为语句生成提速

    • “插入多个值”行为适用于 INSERT 语句

      • 当前支持

      • 禁用该特性

      • 批处理模式操作

      • 将 RETURNING 行与参数集相关联

      • 非批处理模式操作

      • 语句执行模型

      • 控制批处理大小

      • 日志和事件

      • Upsert 支持

    • 引擎释放

    • 与 Driver SQL 和原始 DBAPI 连接一起工作

      • 直接调用驱动程序的 SQL 字符串

      • 直接使用 DBAPI 游标

      • 调用存储过程和用户定义函数

      • 多结果集

    • 注册新方言

      • 进程内注册方言
    • 连接 / 引擎 API

      • Connection

      • CreateEnginePlugin

      • Engine

      • ExceptionContext

      • NestedTransaction

      • RootTransaction

      • Transaction

      • TwoPhaseTransaction

    • 结果集 API

      • ChunkedIteratorResult

      • CursorResult

      • FilterResult

      • FrozenResult

      • IteratorResult

      • MergedResult

      • Result

      • ScalarResult

      • MappingResult

      • Row

      • RowMapping

      • TupleResult

  • 连接池

    • 连接池配置

    • 切换池实现

    • 使用自定义连接函数

    • 构建池

    • 返回时重置

      • 对于非事务连接禁用返回时重置

      • 自定义返回时重置方案

      • 记录返回时重置事件

    • 池事件

    • 处理断开连接

      • 断开处理 - 悲观

      • 断开处理 - 乐观

      • 更多关于失效的信息

      • 支持断开情景下的新数据库错误代码

    • 使用 FIFO vs. LIFO

    • 在多进程或 os.fork() 中使用连接池

    • 直接使用池实例

    • API 文档 - 可用的池实现

      • Pool

      • QueuePool

      • AsyncAdaptedQueuePool

      • SingletonThreadPool

      • AssertionPool

      • NullPool

      • StaticPool

      • ManagesConnection

      • ConnectionPoolEntry

      • PoolProxiedConnection

      • _ConnectionFairy

      • _ConnectionRecord

  • 核心事件

    • 事件

      • Events.dispatch
    • 连接池事件

      • PoolEvents

      • PoolResetState

    • SQL 执行和连接事件

      • ConnectionEvents

      • DialectEvents

    • 模式事件

      • DDLEvents

      • SchemaEventTarget

引擎配置

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

Engine 是任何 SQLAlchemy 应用的起点。它是实际数据库和通过连接池和 Dialect 传递给 SQLAlchemy 应用程序的 DBAPI 的“主基地”,Dialect 描述了如何与特定类型的数据库/DBAPI 组合进行通信。

通用结构可如下所示:

../_images/sqla_engine_arch.png

在上述中,Engine 同时引用了 DialectPool,它们一起解释了 DBAPI 的模块函数以及数据库的行为。

创建引擎只需发出一个单独的调用,create_engine()

from sqlalchemy import create_engine

engine = create_engine("postgresql+psycopg2://scott:tiger@localhost:5432/mydatabase")

上述引擎创建了一个针对 PostgreSQL 定制的 Dialect 对象,以及一个 Pool 对象,当首次收到连接请求时,将在 localhost:5432 建立 DBAPI 连接。请注意,Engine 及其底层的 Pool 在首次调用 Engine.connect()Engine.begin() 方法之前都不会建立第一个实际的 DBAPI 连接。当其他 SQLAlchemy Engine 依赖对象(例如首次需要数据库连接时的 ORM Session 对象)首次需要数据库连接时,也可以调用这些方法中的任何一个。通过这种方式,EnginePool 可以说具有延迟初始化行为。

一旦创建了 Engine,它可以直接用于与数据库交互,也可以传递给一个 Session 对象以与 ORM 一起使用。本节介绍了配置 Engine 的详细信息。下一节,使用 Engines 和 Connections,将详细介绍 Engine 和类似对象的使用 API,通常用于非 ORM 应用程序。

支持的数据库

SQLAlchemy 包含许多不同后端的 Dialect 实现。SQLAlchemy 包含最常见数据库的方言;另外一些需要额外安装单独的方言。

参见 方言 部分了解各种可用后端的信息。## 数据库 URL

create_engine() 函数基于 URL 生成一个 Engine 对象。URL 的格式通常遵循 RFC-1738 的规范,但也有一些例外,包括“scheme”部分中接受下划线而不是破折号或句点。URL 通常包括用户名、密码、主机名、数据库名称字段,以及用于额外配置的可选关键字参数。在某些情况下,可以接受文件路径,而在其他情况下,“数据源名称”取代了“主机”和“数据库”部分。数据库 URL 的典型形式是:

dialect+driver://username:password@host:port/database

方言名称包括 SQLAlchemy 方言的标识名称,例如 sqlitemysqlpostgresqloraclemssql。驱动名称是要使用的 DBAPI 的名称,全部使用小写字母连接到数据库。如果未指定,将导入“默认”DBAPI(如果可用)- 该默认值通常是该后端可用的最广为人知的驱动程序。

转义特殊字符,例如密码中的 @ 符号

构建一个完整的 URL 字符串以传递给 create_engine() 时,特殊字符(如用户和密码中可能使用的字符)需要进行 URL 编码才能正确解析。这包括 @ 符号

下面是一个包含密码 "kx@jj5/g" 的 URL 示例,其中“at”符号和斜杠字符分别表示为 %40%2F

postgresql+pg8000://dbuser:kx%40jj5%2Fg@pghost10/appdb

上述密码的编码可以使用 urllib.parse 生成:

>>> import urllib.parse
>>> urllib.parse.quote_plus("kx@jj5/g")
'kx%40jj5%2Fg'

然后可以将 URL 作为字符串传递给 create_engine()

from sqlalchemy import create_engine

engine = create_engine("postgresql+pg8000://dbuser:kx%40jj5%2Fg@pghost10/appdb")

作为在创建完整的 URL 字符串时转义特殊字符的替代方法,可以将传递给 create_engine() 的对象替换为 URL 对象,它可以直接绕过解析阶段并直接处理未转义的字符串。请参阅下一节的示例。

在 1.4 版本中更改:修复了主机名和数据库名中 @ 符号的支持。由于此修复的副作用,密码中的 @ 符号必须进行转义。

程序化创建 URL

传递给 create_engine() 的值可以是 URL 的一个实例,而不是一个普通字符串,这样可以绕过需要使用字符串解析的步骤,因此不需要提供转义的 URL 字符串。

使用 URL.create() 构造方法创建 URL 对象,通过逐个传递所有字段。密码中的特殊字符可以不做任何修改地传递:

from sqlalchemy import URL

url_object = URL.create(
    "postgresql+pg8000",
    username="dbuser",
    password="kx@jj5/g",  # plain (unescaped) text
    host="pghost10",
    database="appdb",
)

构造的 URL 对象然后可以直接传递给 create_engine(),而不是一个字符串参数:

from sqlalchemy import create_engine

engine = create_engine(url_object)

另见

URL

URL.create()

后端特定的 URLs

下面是常见连接样式的示例。要获取所有包含的方言的详细信息以及第三方方言的链接,请参阅 Dialects。

PostgreSQL

PostgreSQL 方言使用 psycopg2 作为默认的 DBAPI。其他 PostgreSQL DBAPI 包括 pg8000 和 asyncpg:

# default
engine = create_engine("postgresql://scott:tiger@localhost/mydatabase")

# psycopg2
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/mydatabase")

# pg8000
engine = create_engine("postgresql+pg8000://scott:tiger@localhost/mydatabase")

连接到 PostgreSQL 的更多注意事项请参考 PostgreSQL。

MySQL

MySQL 方言使用 mysqlclient 作为默认的 DBAPI。还有其他可用的 MySQL DBAPI,包括 PyMySQL:

# default
engine = create_engine("mysql://scott:tiger@localhost/foo")

# mysqlclient (a maintained fork of MySQL-Python)
engine = create_engine("mysql+mysqldb://scott:tiger@localhost/foo")

# PyMySQL
engine = create_engine("mysql+pymysql://scott:tiger@localhost/foo")

连接到 MySQL 的更多注意事项请参考 MySQL 和 MariaDB。

Oracle

Oracle 方言使用 cx_oracle 作为默认的 DBAPI:

engine = create_engine("oracle://scott:tiger@127.0.0.1:1521/sidname")

engine = create_engine("oracle+cx_oracle://scott:tiger@tnsname")

连接到 Oracle 的更多注意事项请参考 Oracle。

Microsoft SQL Server

SQL Server 方言使用 pyodbc 作为默认的 DBAPI。也可以使用 pymssql:

# pyodbc
engine = create_engine("mssql+pyodbc://scott:tiger@mydsn")

# pymssql
engine = create_engine("mssql+pymssql://scott:tiger@hostname:port/dbname")

连接到 SQL Server 的更多注意事项请参考 Microsoft SQL Server。

SQLite

SQLite 连接到基于文件的数据库,默认情况下使用 Python 内置模块 sqlite3

由于 SQLite 连接到本地文件,URL 格式略有不同。URL 的 “file” 部分是数据库的文件名。对于相对文件路径,这需要三个斜杠:

# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine("sqlite:///foo.db")

对于绝对文件路径,三个斜杠后面跟着绝对路径:

# Unix/Mac - 4 initial slashes in total
engine = create_engine("sqlite:////absolute/path/to/foo.db")

# Windows
engine = create_engine("sqlite:///C:\\path\\to\\foo.db")

# Windows alternative using raw string
engine = create_engine(r"sqlite:///C:\path\to\foo.db")

要使用 SQLite 的 :memory: 数据库,请指定一个空的 URL:

engine = create_engine("sqlite://")

关于连接到 SQLite 的更多注释请参见 SQLite。

其他

请查看方言,其中包含所有额外方言文档的顶级页面。## Engine Creation API

对象名称 描述
create_engine(url, **kwargs) 创建一个新的 Engine 实例。
create_mock_engine(url, executor, **kw) 创建一个用于回显 DDL 的“模拟”引擎。
create_pool_from_url(url, **kwargs) 从给定的 URL 创建一个池实例。
engine_from_config(configuration[, prefix], **kwargs) 使用配置字典创建一个新的 Engine 实例。
make_url(name_or_url) 给定一个字符串,生成一个新的 URL 实例。
URL 表示用于连接到数据库的 URL 的组件。
function sqlalchemy.create_engine(url: str | _url.URL, **kwargs: Any) → Engine

创建一个新的 Engine 实例。

标准调用形式是将 URL 作为第一个位置参数发送,通常是一个指示数据库方言和连接参数的字符串:

engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")

请查阅数据库 URL 以获取编写 URL 字符串的一般指南。特别是,诸如密码中经常出现的特殊字符必须进行 URL 编码才能正确解析。

然后可以跟随它的附加关键字参数,这些参数在结果 Engine 及其底层 DialectPool 构造上建立各种选项:

engine = create_engine("mysql+mysqldb://scott:tiger@hostname/dbname",
                            pool_recycle=3600, echo=True)

URL 的字符串形式是 dialect[+driver]://user:password@host/dbname[?key=value..],其中 dialect 是数据库名称,例如 mysqloraclepostgresql 等,而 driver 是 DBAPI 的名称,例如 psycopg2pyodbccx_oracle 等。或者,URL 可以是 URL 的实例。

**kwargs 接受各种选项,这些选项将被路由到其相应的组件。参数可能是特定于 Engine、底层 Dialect,以及 Pool 的。特定的方言也接受特定于该方言的关键字参数。在这里,我们描述了大多数 create_engine() 使用的参数。

一旦建立,新生成的Engine在调用Engine.connect()或依赖于它的方法(如Engine.execute())时,将从底层的Pool请求连接。当收到此请求时,Pool将建立第一个实际的 DBAPI 连接。create_engine()调用本身不会直接建立任何实际的 DBAPI 连接。

另请参阅

引擎配置

方言

使用引擎和连接

参数:

  • connect_args – 一个字典选项,将作为额外的关键字参数直接传递给 DBAPI 的connect()方法。请参见自定义 DBAPI connect()参数 / on-connect 例程中的示例。

  • creator

    一个可调用的函数,返回一个 DBAPI 连接。此创建函数将传递给底层连接池,并将用于创建所有新的数据库连接。使用此函数会绕过 URL 参数中指定的连接参数。

    这个钩子不像较新的DialectEvents.do_connect()钩子那样灵活,后者允许完全控制如何连接到数据库,考虑到之前的 URL 参数和状态。

    另请参阅

    DialectEvents.do_connect() - 允许完全控制 DBAPI 连接机制的事件钩子。

    自定义 DBAPI connect()参数 / on-connect 例程

  • echo=False

    如果为 True,则 Engine 将记录所有语句以及它们参数列表的repr()到默认的日志处理程序,该处理程序默认为sys.stdout。如果设置为字符串"debug",结果行也将打印到标准输出。Engineecho属性可以随时修改以打开和关闭日志记录;还可以使用标准的 Python logging模块直接控制日志记录。

    另请参阅

    配置日志记录 - 关于如何配置日志记录的更多详细信息。

  • echo_pool=False

    如果为 True,则连接池将记录信息性输出,例如当连接无效时以及当连接被回收时,将输出到默认的日志处理程序,该处理程序默认为sys.stdout。如果设置为字符串"debug",日志记录将包括池的检出和检入。还可以使用标准的 Python logging模块直接控制日志记录。

    另请参阅

    配置日志记录 - 关于如何配置日志记录的更多详细信息。

  • empty_in_strategy

    不再使用;SQLAlchemy 现在在所有情况下都使用“空集合”行为来处理 IN。

    自版本 1.4 弃用:create_engine.empty_in_strategy 关键字已弃用,不再起作用。所有 IN 表达式现在都使用“扩展参数”策略进行渲染,该策略在语句执行时会渲染一组绑定的表达式,或者一个“空集合”SELECT。

  • enable_from_linting

    默认为 True。如果发现给定的 SELECT 语句具有未链接的 FROM 元素,会发出警告,这些元素会导致笛卡尔积。

    新版本 1.4 中新增。

    另请参阅

    内置的 FROM linting 将为 SELECT 语句中的任何潜在笛卡尔积发出警告

  • execution_options – 将应用于所有连接的字典执行选项。参见 Connection.execution_options()

  • future

    使用 2.0 风格的 EngineConnection API。

    从 SQLAlchemy 2.0 开始,此参数仅用于向后兼容,必须保持默认值为 True

    create_engine.future 参数将在随后的 2.x 版本中弃用,并最终被移除。

    新版本 1.4 中新增。

    从版本 2.0 开始更改:所有 Engine 对象都是“未来”风格的引擎,不再有 future=False 的操作模式。

    另请参阅

    SQLAlchemy 2.0 - 主要迁移指南

  • hide_parameters

    布尔值,当设置为 True 时,SQL 语句参数将不会显示在 INFO 日志中,也不会被格式化为 StatementError 对象的字符串表示形式。

    新版本 1.3.8 中新增。

    另请参阅

    配置日志记录 - 关于如何配置日志记录的更多详细信息。

  • implicit_returning=True – 只能设置为 True 的旧参数。在 SQLAlchemy 2.0 中,此参数无效。为了禁用 ORM 调用的语句的“隐式返回”,请在每个表上使用 Table.implicit_returning 参数进行配置。

  • insertmanyvalues_page_size

    当语句使用“insertmanyvalues”模式时,格式化为 INSERT 语句的行数。这是一种分页形式的批量插入,通常与 executemany 执行一起使用,与 RETURNING 一起使用。默认为 1000,但也可能受方言特定的限制因素的影响,这些因素可能会覆盖此值的每个语句的基础。

    2.0 版本中的新内容。

    另请参见

    关于 INSERT 语句的“插入多个值”行为

    控制批量大小

    Connection.execution_options.insertmanyvalues_page_size

  • isolation_level

    无条件地在所有新连接上设置的隔离级别的可选字符串名称。隔离级别通常是字符串名称的一些子集"SERIALIZABLE""REPEATABLE READ""READ COMMITTED""READ UNCOMMITTED""AUTOCOMMIT"根据后端。

    create_engine.isolation_level参数与Connection.execution_options.isolation_level执行选项形成对比,后者可以在单个Connection上设置,以及传递给Engine.execution_options()的相同参数,它可以用于创建具有不同隔离级别的多个引擎,这些引擎共享一个公共的连接池和方言。

    从版本 2.0 开始更改:create_engine.isolation_level参数已被概括为适用于所有支持隔离级别概念的方言,并提供为更简洁、更前置的配置开关,与执行选项形成对比,后者更多是一种临时的编程选项。

    另请参见

    设置事务隔离级别,包括 DBAPI 自动提交

  • json_deserializer

    对于支持JSON数据类型的方言,这是一个将 JSON 字符串转换为 Python 对象的 Python 可调用函数。默认情况下,使用 Python 的json.loads函数。

    从版本 1.3.7 开始更改:SQLite 方言将其从_json_deserializer改名为。

  • json_serializer

    对支持JSON数据类型的方言来说,这是一个 Python 可调用函数,用于将给定对象呈现为 JSON。默认情况下,使用 Python 的json.dumps函数。

    在 1.3.7 版本中更改:SQLite 方言将其从_json_serializer重命名为其他内容。

  • label_length=None

    可选整数值,限制动态生成的列标签的大小到该字符数。如果小于 6,则标签生成为“_(counter)”。如果为None,则使用create_engine.max_identifier_length参数受影响的dialect.max_identifier_length的值。create_engine.label_length的值不能大于create_engine.max_identfier_length的值。

    另请参见

    create_engine.max_identifier_length

  • logging_name

    用于在“sqlalchemy.engine”记录器中生成的日志记录的“name”字段内使用的字符串标识符。默认为对象的 id 的十六进制字符串。

    另请参见

    配置日志 - 关于如何配置日志的进一步详细信息。

    Connection.execution_options.logging_token

  • max_identifier_length

    整数;覆盖方言确定的max_identifier_length。如果为None或零,则无效果。这是数据库配置的最大字符数,可以在 SQL 标识符(如表名、列名或标签名)中使用。所有方言都会自动确定此值,但是如果数据库版本更改了此值但是 SQLAlchemy 的方言没有进行调整,可以在此处传递此值。

    1.3.9 版本中的新内容。

    另请参见

    create_engine.label_length

  • max_overflow=10 – 允许在连接池“溢出”中打开的连接数,即在池大小设置之上可以打开的连接数,默认为五。仅与QueuePool一起使用。

  • module=None – Python 模块对象的引用(模块本身,而不是其字符串名称)。指定引擎方言要使用的替代 DBAPI 模块。每个子方言引用一个特定的 DBAPI,将在首次连接之前导入。此参数导致导入被绕过,并且使用给定模块代替。可以用于测试 DBAPI 以及将“模拟”DBAPI 实现注入到Engine中。

  • paramstyle=None – 渲染绑定参数时要使用的paramstyle。此样式默认为 DBAPI 本身推荐的样式,从 DBAPI 的.paramstyle属性中检索。然而,大多数 DBAPI 接受多种 paramstyle,特别是可能希望将“命名”paramstyle 转换为“位置”paramstyle,反之亦然。当传递此属性时,它应该是值之一"qmark""numeric""named""format""pyformat",并且应该对应于 DBAPI 已知支持的参数样式。

  • pool=None – 已构造的Pool实例,例如QueuePool实例。如果非 None,则此池将直接用作引擎的底层连接池,绕过 URL 参数中存在的任何连接参数。有关手动构建连接池的信息,请参见连接池。

  • poolclass=NonePool子类,将使用 URL 中给定的连接参数创建连接池实例。请注意,这与pool不同,因为在这种情况下您实际上不会实例化池,只是指示要使用哪种类型的池。

  • pool_logging_name

    将在“sqlalchemy.pool”记录生成的“name”字段中使用的字符串标识符。默认为对象的 id 的十六进制字符串。

    另请参见

    配置日志记录 - 关于如何配置日志记录的更多细节。

  • pool_pre_ping

    布尔值,如果为 True,则启用连接池的“预检”功能,即在每次检出时测试连接的活动性。

    1.2 版中的新功能。

    另请参见

    断开处理 - 悲观

  • pool_size=5 – 在连接池中保持打开的连接数。这与QueuePool以及SingletonThreadPool一起使用。对于QueuePool,设置pool_size为 0 表示没有限制;要禁用池化,请将poolclass设置为NullPool

  • pool_recycle=-1

    此设置在给定的秒数过去后导致池回收连接。默认为 -1,或无超时。例如,将其设置为 3600 表示一小时后将回收连接。请注意,特别是 MySQL 将在连接上没有检测到活动时自动断开连接八小时(虽然这可以通过 MySQLDB 连接本身和服务器配置进行配置)。

    另请参阅

    设置池回收

  • pool_reset_on_return='rollback'

    设置底层Pool对象的Pool.reset_on_return参数,可以设置为值"rollback""commit"None

    另请参阅

    归还时重置

  • pool_timeout=30

    从池中获取连接之前等待的秒数。仅与QueuePool一起使用。可以是浮点数,但受 Python 时间函数的限制,可能不可靠达到几十毫秒。

  • pool_use_lifo=False

    QueuePool检索连接时使用 LIFO(后进先出)而不是 FIFO(先进先出)。使用 LIFO,服务器端超时方案可以在非高峰使用期间减少使用的连接数。在规划服务器端超时时,请确保使用回收或预先 ping 策略优雅地处理过时的连接。

    自版本 1.3 新增。

    另请参阅

    使用 FIFO vs. LIFO

    处理断开连接

  • plugins

    要加载的插件名称字符串列表。请参阅CreateEnginePlugin获取背景信息。

    自版本 1.2.3 新增。

  • query_cache_size

    用于缓存查询的 SQL 字符串形式的缓存大小。设置为零以禁用缓存。

    当缓存大小达到 N * 1.5 时,将剪辑其最近最少使用的项。默认为 500,这意味着当填充时,缓存将始终存储至少 500 条 SQL 语句,并且将增长到 750 项,然后通过删除最近最少使用的 250 项将其剪裁回 500。

    缓存是通过生成代表语句结构的缓存键来实现的,然后仅当该键不存在于缓存中时,才为当前方言生成字符串 SQL。所有语句都支持缓存,但是某些功能,例如具有大量参数的 INSERT 操作,将有意绕过缓存。SQL 记录将指示每个语句的统计信息,无论是否从缓存中获取。

    注意

    一些与工作单元持久性相关的 ORM 函数以及一些属性加载策略将使用主缓存之外的每个映射器的个别缓存。

    另请参阅

    SQL 编译缓存

    1.4 版中的新功能。

  • use_insertmanyvalues

    默认为 True,对于 INSERT..RETURNING 语句,默认使用“insertmanyvalues”执行样式。

    2.0 版中的新功能。

    另请参阅

    关于 INSERT 语句的“Insert Many Values”行为

function sqlalchemy.engine_from_config(configuration: Dict[str, Any], prefix: str = 'sqlalchemy.', **kwargs: Any) → Engine

使用配置字典创建一个新的 Engine 实例。

该字典通常由配置文件生成。

对于engine_from_config()感兴趣的键应添加前缀,例如sqlalchemy.urlsqlalchemy.echo等。 ‘prefix’参数表示要搜索的前缀。每个匹配的键(在剥离前缀后)都被视为对create_engine()调用的相应关键字参数。

唯一必需的键是(假设默认前缀为)sqlalchemy.url,它提供了数据库 URL。

一组关键字参数将基于字符串值“强制转换”为其预期类型。每个方言可以使用engine_config_types访问器进行参数集的可扩展。

参数:

  • configuration – 一个字典(通常从配置文件生成,但这不是必需的)。其键以“prefix”值开头的项目将剥离该前缀,然后将传递给create_engine()

  • prefix – 匹配并从‘configuration’中的键中剥离的前缀。

  • kwargsengine_from_config()的每个关键字参数本身都会覆盖从‘configuration’字典中获取的相应项目。关键字参数不应添加前缀。

function sqlalchemy.create_mock_engine(url: str | URL, executor: Any, **kw: Any) → MockConnection

创建用于回显 DDL 的“模拟”引擎。

这是一个用于调试或存储由MetaData.create_all()及其相关方法生成的 DDL 序列输出的实用函数。

该函数接受一个 URL,仅用于确定要使用的方言类型,以及一个“executor”可调用函数,该函数将接收 SQL 表达式对象和参数,然后可以回显或以其他方式打印。执行者的返回值不受处理,引擎也不允许调用常规字符串语句,因此仅在将 DDL 发送到数据库而不接收任何结果时有用。

例如:

from sqlalchemy import create_mock_engine

def dump(sql, *multiparams, **params):
    print(sql.compile(dialect=engine.dialect))

engine = create_mock_engine('postgresql+psycopg2://', dump)
metadata.create_all(engine, checkfirst=False)

参数:

  • url - 一个字符串 URL,通常只需包含数据库后端名称。

  • executor - 一个可调用对象,接收参数sql*multiparams**paramssql参数通常是ExecutableDDLElement的实例,可以使用ExecutableDDLElement.compile()编译成字符串。

版本 1.4 中新增:- create_mock_engine()函数替换了以前与create_engine()一起使用的“模拟”引擎策略。

另请参阅

如何将 CREATE TABLE / DROP TABLE 输出为字符串?

function sqlalchemy.engine.make_url(name_or_url: str | URL) → URL

给定一个字符串,生成一个新的 URL 实例。

URL 的格式通常遵循RFC-1738,但也有一些例外,包括“scheme”部分接受下划线而不是破折号或句点。

如果传递了URL对象,则原样返回。

另请参阅

数据库 URL

function sqlalchemy.create_pool_from_url(url: str | URL, **kwargs: Any) → Pool

从给定的 url 创建一个池实例。

如果未提供poolclass,则使用 URL 中指定的方言选择要使用的池类。

传递给create_pool_from_url()的参数与传递给create_engine()函数的池参数相同。

版本 2.0.10 中新增。

class sqlalchemy.engine.URL

表示用于连接到数据库的 URL 的各个组件。

URL 通常是从完整格式的 URL 字符串构建的,其中make_url()函数在内部由create_engine()函数使用,以将 URL 字符串解析为其各个组件,然后用于构建新的URL对象。当从格式化的 URL 字符串解析时,解析格式通常遵循RFC-1738,但也有一些例外情况。

可以直接生成 URL 对象,方法是使用带有完整形式 URL 字符串的 make_url() 函数,或者使用 URL.create() 构造函数以编程方式给出单个字段来构造 URL。生成的 URL 对象可以直接传递给 create_engine() 以替代字符串参数,这将绕过引擎创建过程中 make_url() 的使用。

从版本 1.4 开始更改:URL 对象现在是不可变的对象。要创建 URL,请使用 make_url()URL.create() 函数/方法。要修改 URL,请使用类似 URL.set()URL.update_query_dict() 的方法返回带有修改的新 URL 对象。有关此更改的注释,请参阅 URL 对象现在是不可变的。

另请参阅

数据库 URL

URL 包含以下属性:

  • URL.drivername: 数据库后端和驱动程序名称,例如 postgresql+psycopg2

  • URL.username: 用户名字符串

  • URL.password: 密码字符串

  • URL.host: 字符串主机名

  • URL.port: 整数端口号

  • URL.database: 字符串数据库名称

  • URL.query: 表示查询字符串的不可变映射。包含键的字符串和值的字符串或字符串元组。

成员

create(), database, difference_update_query(), drivername, get_backend_name(), get_dialect(), get_driver_name(), host, normalized_query, password, port, query, render_as_string(), set(), translate_connect_args(), update_query_dict(), update_query_pairs(), update_query_string(), username

类签名

sqlalchemy.engine.URL (builtins.tuple)

classmethod create(drivername: str, username: str | None = None, password: str | None = None, host: str | None = None, port: int | None = None, database: str | None = None, query: Mapping[str, Sequence[str] | str] = {}) → URL

创建一个新的URL对象。

另请参见

数据库 URL

参数:

  • drivername – 数据库后端的名称。该名称将对应于 sqlalchemy/databases 模块中的一个模块或第三方插件。

  • username – 用户名。

  • password

    数据库密码。通常是一个字符串,但也可以是可以用str()字符串化的对象。

    注意

    当作为参数传递给URL.create()时,密码字符串不应该进行 URL 编码;该字符串应该包含与用户输入一致的密码字符。

    注意

    每个Engine对象只会对密码生成对象进行一次字符串化。对于每次连接的动态密码生成,请参见生成动态认证令牌。

  • host – 主机名。

  • port – 端口号。

  • database – 数据库名称。

  • query – 一个将要传递给方言和/或 DBAPI 的字符串键到字符串值的字典。要直接指定非字符串参数给 Python DBAPI,使用create_engine.connect_args参数传递给create_engine()。另请参见URL.normalized_query,用于一个一致为字符串->字符串列表的字典。

返回:

新的URL对象。

版本 1.4 中的新功能:URL对象现在是不可变命名元组。此外,query字典也是不可变的。要创建 URL,请使用make_url()URL.create()函数/方法。要修改URL,请使用URL.set()URL.update_query()方法。

attribute database: str | None

数据库名称

method difference_update_query(names: Iterable[str]) → URL

URL.query字典中删除给定名称,返回新的URL

例如:

url = url.difference_update_query(['foo', 'bar'])

等同于使用URL.set()如下:

url = url.set(
    query={
        key: url.query[key]
        for key in set(url.query).difference(['foo', 'bar'])
    }
)

版本 1.4 中的新功能。

另请参见

URL.query

URL.update_query_dict()

URL.set()

attribute drivername: str

数据库后端和驱动程序名称,如postgresql+psycopg2

method get_backend_name() → str

返回后端名称。

这是与使用的数据库后端对应的名称,是URL.drivername中加号左侧的部分。

method get_dialect(_is_async: bool = False) → Type[Dialect]

返回与此 URL 的驱动程序名称对应的 SQLAlchemy Dialect类。

method get_driver_name() → str

返回后端名称。

这是与使用的 DBAPI 驱动程序对应的名称,是URL.drivername中加号右侧的部分。

如果URL.drivername不包含加号,则导入此URL的默认Dialect以获取驱动程序名称。

attribute host: str | None

主机名或 IP 地址。对于某些驱动程序,也可以是数据源名称。

attribute normalized_query

返回带有值规范化为序列的URL.query字典。

由于URL.query字典可能包含字符串值或字符串值序列,以区分在查询字符串中多次指定的参数,需要通用处理多个参数的代码将希望使用此属性,以便所有存在的参数都呈现为序列。灵感来自 Python 的urllib.parse.parse_qs函数。例如:

>>> from sqlalchemy.engine import make_url
>>> url = make_url("postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt")
>>> url.query
immutabledict({'alt_host': ('host1', 'host2'), 'ssl_cipher': '/path/to/crt'})
>>> url.normalized_query
immutabledict({'alt_host': ('host1', 'host2'), 'ssl_cipher': ('/path/to/crt',)})
attribute password: str | None

密码,通常是字符串,但也可以是具有__str__()方法的任何对象。

attribute port: int | None

整数端口号

attribute query: immutabledict[str, Tuple[str, ...] | str]

表示查询字符串的不可变映射。键包含字符串,值为字符串或字符串元组,例如:

 >>> from sqlalchemy.engine import make_url
   >>> url = make_url("postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt")
   >>> url.query
   immutabledict({'alt_host': ('host1', 'host2'), 'ssl_cipher': '/path/to/crt'})

To create a mutable copy of this mapping, use the ``dict`` constructor::

   mutable_query_opts = dict(url.query)

另请参阅

URL.normalized_query - 将所有值标准化为序列以进行一致处理。

更改 URL.query 内容的方法:

URL.update_query_dict()

URL.update_query_string()

URL.update_query_pairs()

URL.difference_update_query()

method render_as_string(hide_password: bool = True) → str

渲染此 URL 对象为字符串。

此方法在使用 __str__()__repr__() 方法时使用。该方法直接包括附加选项。

参数:

hide_password – 默认为 True。除非设置为 False,否则不显示密码。

method set(drivername: str | None = None, username: str | None = None, password: str | None = None, host: str | None = None, port: int | None = None, database: str | None = None, query: Mapping[str, Sequence[str] | str] | None = None) → URL

返回具有修改的新 URL 对象。

如果非空则使用值。要明确将值设置为 None,请使用从 namedtuple 转换的 URL._replace() 方法。

参数:

  • drivername – 新驱动名称

  • username – 新的用户名

  • password – 新密码

  • host – 新的主机名

  • port – 新端口

  • query – 新的查询参数,传递一个字符串键的字典,引用字符串或字符串值的序列。完全替换之前的参数列表。

返回:

URL 对象。

版本 1.4 中新增。

另请参阅

URL.update_query_dict()

method translate_connect_args(names: List[str] | None = None, **kw: Any) → Dict[str, Any]

将 url 属性转换为连接参数的字典。

将此 url 的属性(主机、数据库、用户名、密码、端口)作为普通字典返回。默认情况下,属性名称用作键。未设置或为假的属性将从最终字典中省略。

参数:

  • **kw – 可选,用于 url 属性的替代键名。

  • names – 已弃用。与基于关键字的替代名称具有相同目的,但将名称与原始位置相关联。

method update_query_dict(query_parameters: Mapping[str, str | List[str]], append: bool = False) → URL

返回一个新的 URL 对象,其 URL.query 参数字典由给定字典更新。

字典通常包含字符串键和字符串值。为了表示多次表达的查询参数,请传递字符串值序列。

例如:

>>> from sqlalchemy.engine import make_url
>>> url = make_url("postgresql+psycopg2://user:pass@host/dbname")
>>> url = url.update_query_dict({"alt_host": ["host1", "host2"], "ssl_cipher": "/path/to/crt"})
>>> str(url)
'postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt'

参数:

  • query_parameters – 具有字符串键和字符串或字符串序列值的字典。

  • append – 如果为 True,则现有查询字符串中的参数不会被删除;新参数将添加到已有参数之后。如果保持默认值 False,则给定查询参数中的键将替换现有查询字符串中的键。

新版本 1.4 中添加。

另请参阅

URL.query

URL.update_query_string()

URL.update_query_pairs()

URL.difference_update_query()

URL.set()

method update_query_pairs(key_value_pairs: Iterable[Tuple[str, str | List[str]]], append: bool = False) → URL

返回一个新的 URL 对象,其 URL.query 参数字典由给定的键值对序列更新

例如:

>>> from sqlalchemy.engine import make_url
>>> url = make_url("postgresql+psycopg2://user:pass@host/dbname")
>>> url = url.update_query_pairs([("alt_host", "host1"), ("alt_host", "host2"), ("ssl_cipher", "/path/to/crt")])
>>> str(url)
'postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt'

参数:

  • key_value_pairs – 包含两个字符串的元组序列。

  • append – 如果为 True,则现有查询字符串中的参数不会被删除;新参数将添加到已有参数之后。如果保持默认值 False,则给定查询参数中的键将替换现有查询字符串中的键。

新版本 1.4 中添加。

另请参阅

URL.query

URL.difference_update_query()

URL.set()

method update_query_string(query_string: str, append: bool = False) → URL

返回一个新的 URL 对象,其 URL.query 参数字典由给定的查询字符串更新。

例如:

>>> from sqlalchemy.engine import make_url
>>> url = make_url("postgresql+psycopg2://user:pass@host/dbname")
>>> url = url.update_query_string("alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt")
>>> str(url)
'postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt'

参数:

  • query_string – 一个 URL 编码的查询字符串,不包括问号。

  • append – 如果为 True,则现有查询字符串中的参数不会被删除;新参数将添加到已有参数之后。如果保持默认值 False,则给定查询参数中的键将替换现有查询字符串中的键。

新版本 1.4 中添加。

另请参阅

URL.query

URL.update_query_dict()

attribute username: str | None

用户名 字符串

连接池

当调用 connect()execute() 方法时,Engine 将向连接池请求连接。默认的连接池 QueuePool 将根据需要打开到数据库的连接。随着并发语句的执行,QueuePool 将增加其连接池的大小,默认为五个,并允许默认的 "溢出" 十个。由于 Engine 本质上是连接池的“主基地”,因此在应用程序中应该为每个数据库保留一个单独的 Engine,而不是为每个连接创建一个新的。

注意

默认情况下,SQLite 引擎不使用 QueuePool。有关 SQLite 连接池使用的详细信息,请参阅 SQLite。

有关连接池的更多信息,请参阅 连接池。

自定义 DBAPI connect() 参数 / 连接时例程

对于需要特殊连接方法的情况,在绝大多数情况下,最合适的方法是在 create_engine() 级别使用多个钩子来自定义此过程。这些在以下子部分中描述。

通过 dbapi.connect() 传递的特殊关键字参数

所有的 Python DBAPI 都接受除了基本连接之外的额外参数。常见参数包括用于指定字符集编码和超时值的参数;更复杂的数据包括特殊的 DBAPI 常量和对象以及 SSL 子参数。有两种简单的方式可以传递这些参数而不复杂化。

将参数添加到 URL 查询字符串中

简单的字符串值,以及一些数字值和布尔标志,通常可以直接在 URL 的查询字符串中指定。一个常见的例子是接受字符编码参数 encoding 的 DBAPI,例如大多数 MySQL DBAPI:

engine = create_engine("mysql+pymysql://user:pass@host/test?charset=utf8mb4")

使用查询字符串的优点在于可以在配置文件中指定其他的 DBAPI 选项,这样做的方式在 URL 中指定的 DBAPI 方式是可移植的。在此级别传递的具体参数因 SQLAlchemy 方言而异。某些方言将所有参数都作为字符串传递,而其他方言将解析特定的数据类型并将参数移动到不同的位置,例如到驱动程序级别的 DSN 和连接字符串中。由于此领域中方言的行为目前存在差异,因此应该查阅特定方言的文档以查看是否支持在此级别上支持特定参数。

提示

对于给定 URL 显示传递给 DBAPI 的确切参数的一般技术可以直接使用Dialect.create_connect_args()方法进行如下操作:

>>> from sqlalchemy import create_engine
>>> engine = create_engine(
...     "mysql+pymysql://some_user:some_pass@some_host/test?charset=utf8mb4"
... )
>>> args, kwargs = engine.dialect.create_connect_args(engine.url)
>>> args, kwargs
([], {'host': 'some_host', 'database': 'test', 'user': 'some_user', 'password': 'some_pass', 'charset': 'utf8mb4', 'client_flag': 2})

上述的args, kwargs对通常作为dbapi.connect(*args, **kwargs)传递给 DBAPI。

使用 connect_args 字典参数

将任何参数传递给保证在任何时候传递所有参数的dbapi.connect()函数的更通用的系统是create_engine.connect_args字典参数。这可用于否则不被方言处理的参数添加到查询字符串时,以及当需要将特殊子结构或对象传递给 DBAPI 时。有时只是需要将特定标志发送为True符号,而 SQLAlchemy 方言并不知道如何将其从 URL 中呈现的字符串形式强制为此关键字参数。下面说明了使用取代连接的基础实现的 psycopg2“连接工厂”的用法:

engine = create_engine(
    "postgresql+psycopg2://user:pass@hostname/dbname",
    connect_args={"connection_factory": MyConnectionFactory},
)

另一个示例是 pyodbc 的“timeout”参数:

engine = create_engine(
    "mssql+pyodbc://user:pass@sqlsrvr?driver=ODBC+Driver+13+for+SQL+Server",
    connect_args={"timeout": 30},
)

上述示例还说明了 URL“查询字符串”参数以及create_engine.connect_args都可以同时使用;在 pyodbc 的情况下,“driver”关键字在 URL 中具有特殊含义。

控制参数传递给 DBAPI connect()函数的方式

除了操作传递给connect()的参数之外,我们还可以使用DialectEvents.do_connect()事件挂钩进一步定制如何调用 DBAPI connect()函数本身。此挂钩将传递完整的*args, **kwargs,方言将发送到connect()。然后,可以在原地修改这些集合以更改它们的使用方式:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "do_connect")
def receive_do_connect(dialect, conn_rec, cargs, cparams):
    cparams["connection_factory"] = MyConnectionFactory

生成动态认证令牌

DialectEvents.do_connect()也是一个理想的方法,可以动态插入可能在Engine生命周期内更改的认证令牌。例如,如果令牌由get_authentication_token()生成并作为token参数传递给 DBAPI,则可以实现如下:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user@hostname/dbname")

@event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    cparams["token"] = get_authentication_token()

另请参见

使用访问令牌连接到数据库 - 一个涉及 SQL Server 的更具体的示例

修改连接后的 DBAPI 连接或在连接后运行命令

对于 SQLAlchemy 创建的 DBAPI 连接,没有问题,但我们希望在实际使用之前修改完成的连接,例如设置特殊标志或运行某些命令,PoolEvents.connect() 事件钩子是最合适的钩子。这个钩子在每次创建新连接时都会被调用,在 SQLAlchemy 使用之前:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "connect")
def connect(dbapi_connection, connection_record):
    cursor_obj = dbapi_connection.cursor()
    cursor_obj.execute("SET some session variables")
    cursor_obj.close()

完全替换 DBAPI 的 connect() 函数

最后,DialectEvents.do_connect() 事件钩子也可以允许我们完全接管连接过程,建立连接并返回它:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "do_connect")
def receive_do_connect(dialect, conn_rec, cargs, cparams):
    # return the new DBAPI connection with whatever we'd like to
    # do
    return psycopg2.connect(*cargs, **cparams)

DialectEvents.do_connect() 钩子取代了以前的 create_engine.creator 钩子,但前者仍然可用。DialectEvents.do_connect() 具有一个明显的优势,就是解析自 URL 的完整参数也会传递给用户定义的函数,而这在 create_engine.creator 中不是这样的。 ## 配置日志记录

Python 的标准 logging 模块用于实现 SQLAlchemy 的信息和调试日志输出。这使得 SQLAlchemy 的日志记录可以以标准方式与其他应用程序和库集成。create_engine() 还有两个参数 create_engine.echocreate_engine.echo_pool,允许立即将日志记录到 sys.stdout 以便进行本地开发;这些参数最终会与下面描述的常规 Python 记录器交互。

本节假设您熟悉上面链接的日志记录模块。SQLAlchemy 所执行的所有日志记录都存在于 sqlalchemy 命名空间下,就像 logging.getLogger('sqlalchemy') 一样。当配置了日志记录(例如通过 logging.basicConfig()),可以打开的 SA 日志记录器的通用命名空间如下:

  • sqlalchemy.engine - 控制 SQL 回显。设置为 logging.INFO 以输出 SQL 查询,设置为 logging.DEBUG 以输出查询 + 结果集。这些设置等同于 create_engine.echo 上的 echo=Trueecho="debug"

  • sqlalchemy.pool - 控制连接池日志记录。设置为 logging.INFO 以记录连接失效和重用事件;设置为 logging.DEBUG 以另外记录所有池的签入和签出。这些设置等同于在 create_engine.echo_pool 上分别设置 pool_echo=Truepool_echo="debug"

  • sqlalchemy.dialects - 控制用于 SQL 方言的自定义日志记录,日志记录程度在特定方言中使用的情况下通常很少。

  • sqlalchemy.orm - 控制在 ORM 中使用日志记录的各种 ORM 函数的日志记录程度,通常很少。设置为 logging.INFO 以记录一些关于映射器配置的顶级信息。

例如,要使用 Python 日志记录而不是 echo=True 标志来记录 SQL 查询:

import logging

logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)

默认情况下,整个 sqlalchemy 命名空间中的日志级别设置为 logging.WARN,以便即使在已启用日志记录的应用程序中,也不会发生任何日志操作。

注意

SQLAlchemy Engine 通过仅在检测到当前日志级别为 logging.INFOlogging.DEBUG 时发出日志语句来节省 Python 函数调用开销。它仅在从连接池获取新连接时检查此级别。因此,在已经运行的应用程序中更改日志配置时,任何当前活动的 Connection(通常更常见的是活动事务中的 Session 对象)将根据新配置不会记录任何 SQL,直到获取新的 Connection(对于 Session,这是在当前事务结束并开始新事务之后)。

关于回显标志的更多信息

如前所述,create_engine.echocreate_engine.echo_pool 参数是立即记录到 sys.stdout 的快捷方式:

>>> from sqlalchemy import create_engine, text
>>> e = create_engine("sqlite://", echo=True, echo_pool="debug")
>>> with e.connect() as conn:
...     print(conn.scalar(text("select 'hi'")))
2020-10-24 12:54:57,701 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Created new connection <sqlite3.Connection object at 0x7f287819ac60>
2020-10-24 12:54:57,701 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> checked out from pool
2020-10-24 12:54:57,702 INFO sqlalchemy.engine.Engine select 'hi'
2020-10-24 12:54:57,702 INFO sqlalchemy.engine.Engine ()
hi
2020-10-24 12:54:57,703 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> being returned to pool
2020-10-24 12:54:57,704 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> rollback-on-return

使用这些标志大致相当于:

import logging

logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
logging.getLogger("sqlalchemy.pool").setLevel(logging.DEBUG)

需要注意的是,这两个标志独立于任何现有的日志配置,并且将无条件使用 logging.basicConfig()。这会在任何现有的记录器配置之外额外进行配置。因此,在明确配置日志记录时,请始终确保所有回显标志始终设置为 False,以避免获得重复的日志行。

设置日志名称

实例的记录器名称,例如 EnginePool 的默认值为使用截断的十六进制标识符字符串。要将其设置为特定名称,请使用 create_engine.logging_namecreate_engine.pool_logging_namesqlalchemy.create_engine();名称将附加到记录名称 sqlalchemy.engine.Engine

>>> import logging
>>> from sqlalchemy import create_engine
>>> from sqlalchemy import text
>>> logging.basicConfig()
>>> logging.getLogger("sqlalchemy.engine.Engine.myengine").setLevel(logging.INFO)
>>> e = create_engine("sqlite://", logging_name="myengine")
>>> with e.connect() as conn:
...     conn.execute(text("select 'hi'"))
2020-10-24 12:47:04,291 INFO sqlalchemy.engine.Engine.myengine select 'hi'
2020-10-24 12:47:04,292 INFO sqlalchemy.engine.Engine.myengine ()

提示

create_engine.logging_namecreate_engine.pool_logging_name 参数也可以与 create_engine.echocreate_engine.echo_pool 一起使用。但是,如果其他引擎的回声标志设置为 True,而没有记录名称,则将发生不可避免的双重记录条件。这是因为将自动为 sqlalchemy.engine.Engine 添加一个处理程序,该处理程序将同时记录无名称引擎和具有记录名称的引擎的消息。例如:

from sqlalchemy import create_engine, text

e1 = create_engine("sqlite://", echo=True, logging_name="myname")
with e1.begin() as conn:
    conn.execute(text("SELECT 1"))

e2 = create_engine("sqlite://", echo=True)
with e2.begin() as conn:
    conn.execute(text("SELECT 2"))

with e1.begin() as conn:
    conn.execute(text("SELECT 3"))

上述场景将双重记录 SELECT 3。要解决此问题,请确保所有引擎都设置了 logging_name,或者使用显式记录器/处理程序设置,而不使用 create_engine.echocreate_engine.echo_pool

设置每个连接/子引擎令牌

1.4.0b2 版本中的新功能。

当记录名称适合于在长时间存在的 Engine 对象上建立时,它并不灵活到足以容纳任意大的名称列表,用于跟踪日志消息中的单个连接和/或事务的情况。

对于这种用例,由 ConnectionResult 对象生成的日志消息本身可以使用其他令牌进行增强,例如事务或请求标识符。 Connection.execution_options.logging_token 参数接受一个字符串参数,该参数可用于建立每个连接的跟踪令牌:

>>> from sqlalchemy import create_engine
>>> e = create_engine("sqlite://", echo="debug")
>>> with e.connect().execution_options(logging_token="track1") as conn:
...     conn.execute(text("select 1")).all()
2021-02-03 11:48:45,754 INFO sqlalchemy.engine.Engine [track1] select 1
2021-02-03 11:48:45,754 INFO sqlalchemy.engine.Engine [track1] [raw sql] ()
2021-02-03 11:48:45,754 DEBUG sqlalchemy.engine.Engine [track1] Col ('1',)
2021-02-03 11:48:45,755 DEBUG sqlalchemy.engine.Engine [track1] Row (1,)

Connection.execution_options.logging_token参数也可以通过create_engine.execution_optionsEngine.execution_options()在引擎或子引擎上建立。这可能对应用程序的不同组件应用不同的日志令牌而无需创建新引擎很有用:

>>> from sqlalchemy import create_engine
>>> e = create_engine("sqlite://", echo="debug")
>>> e1 = e.execution_options(logging_token="track1")
>>> e2 = e.execution_options(logging_token="track2")
>>> with e1.connect() as conn:
...     conn.execute(text("select 1")).all()
2021-02-03 11:51:08,960 INFO sqlalchemy.engine.Engine [track1] select 1
2021-02-03 11:51:08,960 INFO sqlalchemy.engine.Engine [track1] [raw sql] ()
2021-02-03 11:51:08,960 DEBUG sqlalchemy.engine.Engine [track1] Col ('1',)
2021-02-03 11:51:08,961 DEBUG sqlalchemy.engine.Engine [track1] Row (1,)

>>> with e2.connect() as conn:
...     conn.execute(text("select 2")).all()
2021-02-03 11:52:05,518 INFO sqlalchemy.engine.Engine [track2] Select 1
2021-02-03 11:52:05,519 INFO sqlalchemy.engine.Engine [track2] [raw sql] ()
2021-02-03 11:52:05,520 DEBUG sqlalchemy.engine.Engine [track2] Col ('1',)
2021-02-03 11:52:05,520 DEBUG sqlalchemy.engine.Engine [track2] Row (1,)

隐藏参数

Engine发出的日志还指示了特定语句中存在的 SQL 参数的摘录。为了防止出于隐私目的记录这些参数,启用create_engine.hide_parameters标志:

>>> e = create_engine("sqlite://", echo=True, hide_parameters=True)
>>> with e.connect() as conn:
...     conn.execute(text("select :some_private_name"), {"some_private_name": "pii"})
2020-10-24 12:48:32,808 INFO sqlalchemy.engine.Engine select ?
2020-10-24 12:48:32,808 INFO sqlalchemy.engine.Engine [SQL parameters hidden due to hide_parameters=True]
```  ## 支持的数据库

SQLAlchemy 包括许多用于各种后端的`Dialect`实现。SQLAlchemy 包含最常见数据库的方言;另外一些需要额外安装单独的方言。

查看 Dialects 部分,了解可用的各种后端信息。

## 数据库 URL

`create_engine()`函数基于 URL 生成一个`Engine`对象。URL 的格式通常遵循[RFC-1738](https://www.ietf.org/rfc/rfc1738.txt),但也有一些例外,包括“scheme”部分接受下划线而不是破折号或句点。URL 通常包括用户名、密码、主机名、数据库名字段,以及用于额外配置的可选关键字参数。在某些情况下,接受文件路径,而在其他情况下,“数据源名称”取代“主机”和“数据库”部分。数据库 URL 的典型形式为:

```py
dialect+driver://username:password@host:port/database

方言名称包括 SQLAlchemy 方言的标识名称,如sqlitemysqlpostgresqloraclemssql。驱动程序名称是用于使用所有小写字母连接到数据库的 DBAPI 的名称。如果未指定,将导入“默认”DBAPI(如果可用)- 这个默认通常是该后端可用的最广为人知的驱动程序。

转义特殊字符,如密码中的@符号

在构建完整的 URL 字符串以传递给create_engine()时,特殊字符(如用户和密码中可能使用的字符)需要进行 URL 编码以正确解析。这包括@符号

以下是包含密码 "kx@jj5/g" 的 URL 的示例,其中“at” 符号和斜杠字符分别表示为 %40%2F

postgresql+pg8000://dbuser:kx%40jj5%2Fg@pghost10/appdb

上述密码的编码可以使用 urllib.parse 生成:

>>> import urllib.parse
>>> urllib.parse.quote_plus("kx@jj5/g")
'kx%40jj5%2Fg'

然后 URL 可以作为字符串传递给 create_engine()

from sqlalchemy import create_engine

engine = create_engine("postgresql+pg8000://dbuser:kx%40jj5%2Fg@pghost10/appdb")

作为在创建完整 URL 字符串时替代转义特殊字符的选择,传递给 create_engine() 的对象可以是 URL 对象的实例,它绕过了解析阶段,可以直接容纳未转义的字符串。查看下一节以获取示例。

1.4 版本中的变化:已修复主机名和数据库名中 @ 符号的支持。作为此修复的副作用,密码中的 @ 符号必须转义。

以编程方式创建 URL

传递给 create_engine() 的值可以是 URL 的实例,而不是简单的字符串,这样可以绕过使用字符串解析的需要,因此不需要提供转义的 URL 字符串。

使用 URL.create() 构造方法创建 URL 对象,逐个传递所有字段。密码中的特殊字符可以不作任何修改地传递:

from sqlalchemy import URL

url_object = URL.create(
    "postgresql+pg8000",
    username="dbuser",
    password="kx@jj5/g",  # plain (unescaped) text
    host="pghost10",
    database="appdb",
)

构造的 URL 对象然后可以直接传递给 create_engine() 代替字符串参数:

from sqlalchemy import create_engine

engine = create_engine(url_object)

另请参阅

URL

URL.create()

特定后端的 URL

下面是常见连接样式的示例。有关所有包含方言的详细信息以及第三方方言的链接的完整索引,请参见 方言。

PostgreSQL

PostgreSQL 方言使用 psycopg2 作为默认的 DBAPI。其他 PostgreSQL DBAPI 包括 pg8000 和 asyncpg:

# default
engine = create_engine("postgresql://scott:tiger@localhost/mydatabase")

# psycopg2
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/mydatabase")

# pg8000
engine = create_engine("postgresql+pg8000://scott:tiger@localhost/mydatabase")

关于连接到 PostgreSQL 的更多说明请参见 PostgreSQL。

MySQL

MySQL 方言使用 mysqlclient 作为默认的 DBAPI。还有其他可用的 MySQL DBAPI,包括 PyMySQL:

# default
engine = create_engine("mysql://scott:tiger@localhost/foo")

# mysqlclient (a maintained fork of MySQL-Python)
engine = create_engine("mysql+mysqldb://scott:tiger@localhost/foo")

# PyMySQL
engine = create_engine("mysql+pymysql://scott:tiger@localhost/foo")

关于连接到 MySQL 的更多说明请参见 MySQL 和 MariaDB。

Oracle

Oracle 方言使用 cx_oracle 作为默认的 DBAPI:

engine = create_engine("oracle://scott:tiger@127.0.0.1:1521/sidname")

engine = create_engine("oracle+cx_oracle://scott:tiger@tnsname")

关于连接到 Oracle 的更多说明请参见 Oracle。

Microsoft SQL Server

SQL Server 方言使用 pyodbc 作为默认的 DBAPI。pymssql 也可用:

# pyodbc
engine = create_engine("mssql+pyodbc://scott:tiger@mydsn")

# pymssql
engine = create_engine("mssql+pymssql://scott:tiger@hostname:port/dbname")

关于连接到 SQL Server 的更多说明请参见 Microsoft SQL Server。

SQLite

SQLite 连接到基于文件的数据库,默认使用 Python 内置模块sqlite3

由于 SQLite 连接到本地文件,URL 格式略有不同。URL 的“file”部分是数据库的文件名。对于相对文件路径,这需要三个斜杠:

# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine("sqlite:///foo.db")

对于绝对文件路径,三个斜杠后跟绝对路径:

# Unix/Mac - 4 initial slashes in total
engine = create_engine("sqlite:////absolute/path/to/foo.db")

# Windows
engine = create_engine("sqlite:///C:\\path\\to\\foo.db")

# Windows alternative using raw string
engine = create_engine(r"sqlite:///C:\path\to\foo.db")

要使用 SQLite :memory:数据库,请指定空 URL:

engine = create_engine("sqlite://")

关于连接到 SQLite 的更多说明请参见 SQLite。

其他

请参阅 Dialects,所有其他方言文档的顶级页面。

转义密码中的特殊字符,例如@符号

在构造完整的 URL 字符串以传递给create_engine()时,需要对特殊字符进行 URL 编码才能正确解析这包括@符号

下面是一个包含密码"kx@jj5/g"的 URL 示例,其中“at”符号和斜杠字符分别表示为%40%2F

postgresql+pg8000://dbuser:kx%40jj5%2Fg@pghost10/appdb

上述密码的编码可以使用urllib.parse生成:

>>> import urllib.parse
>>> urllib.parse.quote_plus("kx@jj5/g")
'kx%40jj5%2Fg'

然后 URL 可以作为字符串传递给create_engine()

from sqlalchemy import create_engine

engine = create_engine("postgresql+pg8000://dbuser:kx%40jj5%2Fg@pghost10/appdb")

作为在创建完整 URL 字符串时转义特殊字符的替代方法,传递给create_engine()的对象可以是URL对象的实例,它会绕过解析阶段并可以直接适应未转义的字符串。请参阅下一节的示例。

从版本 1.4 开始更改:修复了对主机名和数据库名中的@符号的支持。由于此修复的副作用,密码中的@符号必须转义。

以编程方式创建 URL

传递给create_engine()的值可以是URL的实例,而不是简单的字符串,这将绕过使用字符串解析的需要,因此不需要提供已转义的 URL 字符串。

URL对象是使用URL.create()构造方法创建的,分别传递所有字段。例如密码中的特殊字符可以直接传递而无需任何修改:

from sqlalchemy import URL

url_object = URL.create(
    "postgresql+pg8000",
    username="dbuser",
    password="kx@jj5/g",  # plain (unescaped) text
    host="pghost10",
    database="appdb",
)

然后构造的URL对象可以直接传递给create_engine()以替代字符串参数:

from sqlalchemy import create_engine

engine = create_engine(url_object)

另请参阅

URL

URL.create()

后端特定的 URL

下面是常见连接样式的示例。有关所有包含方言的详细信息以及链接到第三方方言的链接的完整索引,请参阅方言。

PostgreSQL

PostgreSQL 方言默认使用 psycopg2 作为默认的 DBAPI。其他 PostgreSQL DBAPI 包括 pg8000 和 asyncpg:

# default
engine = create_engine("postgresql://scott:tiger@localhost/mydatabase")

# psycopg2
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/mydatabase")

# pg8000
engine = create_engine("postgresql+pg8000://scott:tiger@localhost/mydatabase")

在 PostgreSQL 中有关连接到 PostgreSQL 的更多注意事项。

MySQL

MySQL 方言默认使用 mysqlclient 作为默认的 DBAPI。还有其他可用的 MySQL DBAPI,包括 PyMySQL:

# default
engine = create_engine("mysql://scott:tiger@localhost/foo")

# mysqlclient (a maintained fork of MySQL-Python)
engine = create_engine("mysql+mysqldb://scott:tiger@localhost/foo")

# PyMySQL
engine = create_engine("mysql+pymysql://scott:tiger@localhost/foo")

在 MySQL 和 MariaDB 中有关连接到 MySQL 的更多注意事项。

Oracle

Oracle 方言默认使用 cx_oracle 作为默认的 DBAPI:

engine = create_engine("oracle://scott:tiger@127.0.0.1:1521/sidname")

engine = create_engine("oracle+cx_oracle://scott:tiger@tnsname")

在 Oracle 中有关连接到 Oracle 的更多注意事项。

Microsoft SQL Server

SQL Server 方言默认使用 pyodbc 作为默认的 DBAPI。pymssql 也可用:

# pyodbc
engine = create_engine("mssql+pyodbc://scott:tiger@mydsn")

# pymssql
engine = create_engine("mssql+pymssql://scott:tiger@hostname:port/dbname")

在 Microsoft SQL Server 中有关连接到 SQL Server 的更多注意事项。

SQLite

SQLite 连接到基于文件的数据库,默认情况下使用 Python 内置模块sqlite3

由于 SQLite 连接到本地文件,URL 格式略有不同。 URL 的“file”部分是数据库的文件名。对于相对文件路径,这需要三个斜杠:

# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine("sqlite:///foo.db")

对于绝对文件路径,三个斜杠后跟绝对路径:

# Unix/Mac - 4 initial slashes in total
engine = create_engine("sqlite:////absolute/path/to/foo.db")

# Windows
engine = create_engine("sqlite:///C:\\path\\to\\foo.db")

# Windows alternative using raw string
engine = create_engine(r"sqlite:///C:\path\to\foo.db")

要使用 SQLite :memory: 数据库,请指定一个空 URL:

engine = create_engine("sqlite://")

在 SQLite 中有关连接到 SQLite 的更多注意事项。

其他

请参阅方言,这是所有额外方言文档的顶级页面。

PostgreSQL

PostgreSQL 方言默认使用 psycopg2 作为默认的 DBAPI。其他 PostgreSQL DBAPI 包括 pg8000 和 asyncpg:

# default
engine = create_engine("postgresql://scott:tiger@localhost/mydatabase")

# psycopg2
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/mydatabase")

# pg8000
engine = create_engine("postgresql+pg8000://scott:tiger@localhost/mydatabase")

在 PostgreSQL 中有关连接到 PostgreSQL 的更多注意事项。

MySQL

MySQL 方言默认使用 mysqlclient 作为默认的 DBAPI。还有其他可用的 MySQL DBAPI,包括 PyMySQL:

# default
engine = create_engine("mysql://scott:tiger@localhost/foo")

# mysqlclient (a maintained fork of MySQL-Python)
engine = create_engine("mysql+mysqldb://scott:tiger@localhost/foo")

# PyMySQL
engine = create_engine("mysql+pymysql://scott:tiger@localhost/foo")

在 MySQL 和 MariaDB 中有关连接到 MySQL 的更多注意事项。

Oracle

Oracle 方言默认使用 cx_oracle 作为默认的 DBAPI:

engine = create_engine("oracle://scott:tiger@127.0.0.1:1521/sidname")

engine = create_engine("oracle+cx_oracle://scott:tiger@tnsname")

在 Oracle 中有关连接到 Oracle 的更多注意事项。

Microsoft SQL Server

SQL Server 方言默认使用 pyodbc 作为默认的 DBAPI。pymssql 也可用:

# pyodbc
engine = create_engine("mssql+pyodbc://scott:tiger@mydsn")

# pymssql
engine = create_engine("mssql+pymssql://scott:tiger@hostname:port/dbname")

在 Microsoft SQL Server 中有关连接到 SQL Server 的更多注意事项。

SQLite

SQLite 连接到基于文件的数据库,默认情况下使用 Python 内置模块sqlite3

由于 SQLite 连接到本地文件,URL 格式略有不同。 URL 的“file”部分是数据库的文件名。对于相对文件路径,这需要三个斜杠:

# sqlite://<nohostname>/<path>
# where <path> is relative:
engine = create_engine("sqlite:///foo.db")

对于绝对文件路径,三个斜杠后跟绝对路径:

# Unix/Mac - 4 initial slashes in total
engine = create_engine("sqlite:////absolute/path/to/foo.db")

# Windows
engine = create_engine("sqlite:///C:\\path\\to\\foo.db")

# Windows alternative using raw string
engine = create_engine(r"sqlite:///C:\path\to\foo.db")

要使用 SQLite :memory: 数据库,请指定一个空 URL:

engine = create_engine("sqlite://")

在 SQLite 中有关连接到 SQLite 的更多注意事项。

其他

请参阅方言,这是所有额外方言文档的顶级页面。

引擎创建 API

对象名称 描述
create_engine(url, **kwargs) 创建一个新的 Engine 实例。
create_mock_engine(url, executor, **kw) 创建用于回显 DDL 的“模拟”引擎。
create_pool_from_url(url, **kwargs) 根据给定的 URL 创建一个池实例。
engine_from_config(configuration[, prefix], **kwargs) 使用配置字典创建一个新的 Engine 实例。
make_url(name_or_url) 给定一个字符串,生成一个新的 URL 实例。
URL 表示用于连接到数据库的 URL 的组件。
function sqlalchemy.create_engine(url: str | _url.URL, **kwargs: Any) → Engine

创建一个新的 Engine 实例。

标准的调用形式是将 URL 作为第一个位置参数发送,通常是一个指示数据库方言和连接参数的字符串:

engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")

注意

请参阅数据库 URL 以获取编写 URL 字符串的一般准则。特别是,特殊字符(例如密码中经常包含的字符)必须进行 URL 编码才能正确解析。

随后可以跟随其他关键字参数,这些参数将在生成的 Engine 及其基础的 DialectPool 构造上建立各种选项:

engine = create_engine("mysql+mysqldb://scott:tiger@hostname/dbname",
                            pool_recycle=3600, echo=True)

URL 的字符串形式为 dialect[+driver]://user:password@host/dbname[?key=value..],其中 dialect 是数据库名称,例如 mysqloraclepostgresql 等,而 driver 是 DBAPI 的名称,例如 psycopg2pyodbccx_oracle 等。另外,URL 还可以是 URL 的实例。

**kwargs 接受各种路由到其适当组件的选项。参数可能特定于 Engine、底层 Dialect,以及 Pool。特定方言还接受特定于该方言的关键字参数。在这里,我们描述了大多数 create_engine() 使用的通用参数。

一旦建立,新生成的 Engine 将在调用 Engine.connect() 或依赖于它的方法,例如调用 Engine.execute() 时从底层 Pool 请求连接。当收到此请求时,Pool 反过来将建立第一个实际的 DBAPI 连接。 create_engine() 调用本身不会直接建立任何实际的 DBAPI 连接。

另请参阅

引擎配置

方言

使用引擎和连接

参数:

  • connect_args – 一个选项字典,将作为额外的关键字参数直接传递给 DBAPI 的 connect() 方法。请参阅自定义 DBAPI connect() 参数 / on-connect routines 的示例。

  • creator

    返回 DBAPI 连接的可调用对象。此创建函数将传递给底层连接池,并将用于创建所有新的数据库连接。使用此函数会使 URL 参数中指定的连接参数被绕过。

    此钩子不如较新的 DialectEvents.do_connect() 钩子灵活,后者允许完全控制在数据库中如何建立连接,给定全套 URL 参数和先前的状态。

    另请参阅

    DialectEvents.do_connect() - 事件钩子,允许完全控制 DBAPI 连接机制。

    自定义 DBAPI connect() 参数 / on-connect routines

  • echo=False

    如果为 True,则引擎将记录所有语句以及它们参数列表的repr()到默认的日志处理程序,默认情况下为sys.stdout输出。如果设置为字符串"debug",则结果行也将打印到标准输出。Engineecho属性可以随时修改以打开和关闭日志记录;也可以使用标准的 Python logging 模块直接控制日志记录。

    另请参阅

    配置日志记录 - 如何配置日志记录的更多细节。

  • echo_pool=False

    如果为 True,则连接池将记录信息输出,例如当连接无效时以及当连接被回收到默认日志处理程序时,该默认日志处理程序默认为sys.stdout用于输出。如果设置为字符串"debug",则日志将包括池检出和签入。也可以使用标准的 Python logging 模块进行日志记录。

    另请参阅

    配置日志记录 - 关于如何配置日志记录的更多详细信息。

  • empty_in_strategy

    不再使用;SQLAlchemy 现在在所有情况下都使用“空集”行为来处理 IN。

    自 1.4 版本起弃用:create_engine.empty_in_strategy关键字已弃用,不再起作用。现在所有 IN 表达式都使用“扩展参数”策略进行渲染,在语句执行时渲染一组 boundexpressions,或者一个“空集”SELECT。

  • enable_from_linting

    默认为 True。如果发现给定的 SELECT 语句具有未链接的 FROM 元素,可能导致笛卡尔积,则会发出警告。

    1.4 版本中的新功能。

    另请参阅

    内置的 FROM linting 将警告任何 SELECT 语句中可能存在的笛卡尔积

  • execution_options – 将应用于所有连接的字典执行选项。参见Connection.execution_options()

  • future

    使用 2.0 风格的EngineConnection API。

    从 SQLAlchemy 2.0 开始,此���数仅用于向后兼容,必须保持其默认值为True

    create_engine.future参数将在随后的 2.x 版本中被弃用,并最终移除。

    1.4 版本中的新功能。

    2.0 版本中的更改:所有Engine对象都是“future”风格的引擎,不再有future=False操作模式。

    另请参阅

    SQLAlchemy 2.0 - 主要迁移指南

  • hide_parameters

    布尔值,当设置为 True 时,SQL 语句参数将不会显示在 INFO 日志中,也不会被格式化为StatementError对象的字符串表示。

    1.3.8 版本中的新功能。

    另请参阅

    配置日志记录 - 关于如何配置日志记录的更多详细信息。

  • implicit_returning=True – 旧参数,只能设置为 True。在 SQLAlchemy 2.0 中,此参数不起作用。为了禁用 ORM 调用的语句的“隐式返回”,请在每个表上使用Table.implicit_returning参数进行配置。

  • insertmanyvalues_page_size

    在使用“insertmanyvalues”模式的语句格式化为 INSERT 语句时,插入的行数。该模式是用于许多后端的分页形式的批量插入,在使用 executemany 执行时通常与 RETURNING 一起使用。默认为 1000,但也可能受到方言特定的限制因素的影响,这些因素可能会在每个语句的基础上覆盖此值。

    自 2.0 版本起新增。

    另请参见

    “插入多个值”INSERT 语句行为

    控制批量大小

    Connection.execution_options.insertmanyvalues_page_size

  • isolation_level

    必要时,所有新连接上都将设置的隔离级别的可选字符串名称。隔离级别通常是基于后端的字符串名称"SERIALIZABLE""REPEATABLE READ""READ COMMITTED""READ UNCOMMITTED""AUTOCOMMIT"的某些子集。

    create_engine.isolation_level参数与Connection.execution_options.isolation_level执行选项形成对比,后者可在单个Connection上设置,以及传递给Engine.execution_options()的相同参数,在那里可以用来创建具有不同隔离级别的多个引擎,这些引擎共享一个公共连接池和方言。

    从 2.0 版本开始更改:create_engine.isolation_level参数已泛化,适用于所有支持隔离级别概念的方言,并提供作为更简洁、直接的配置开关,与执行选项形成对比,后者更像是一种临时编程选项。

    另请参见

    设置事务隔离级别,包括 DBAPI 自动提交

  • json_deserializer

    对于支持JSON数据类型的方言,这是一个 Python 可调用对象,它将 JSON 字符串转换为 Python 对象。默认情况下,使用 Python 的json.loads函数。

    从 1.3.7 版本开始更改:SQLite 方言将其从_json_deserializer改名为此。

  • json_serializer

    对于支持 JSON 数据类型的方言,这是一个 Python 可调用函数,它将给定对象呈现为 JSON。默认情况下,使用 Python 的 json.dumps 函数。

    从版本 1.3.7 开始更改:SQLite 方言将其从 _json_serializer 重命名为 _json_serializer

  • label_length=None -

    可选整数值,将动态生成的列标签的大小限制为该数目的字符数。如果小于 6,则标签将生成为“_(计数器)”。如果为 None,则使用 dialect.max_identifier_length 的值,可以通过 create_engine.max_identifier_length 参数进行设置。create_engine.label_length 的值不能大于 create_engine.max_identfier_length 的值。

    另请参阅

    create_engine.max_identifier_length

  • logging_name -

    用于在 “sqlalchemy.engine” 记录器中生成的日志记录的 “name” 字段中使用的字符串标识符。默认为对象 id 的十六进制字符串。

    另请参阅

    配置日志记录 - 关于如何配置日志记录的更多详细信息。

    Connection.execution_options.logging_token

  • max_identifier_length -

    整数;覆盖由方言确定的 max_identifier_length。如果为 None 或零,则无效。这是数据库配置的最大字符数,可用于 SQL 标识符(如表名、列名或标签名)。所有方言都会自动确定此值,但是在新数据库版本中,此值已更改但未调整 SQLAlchemy 方言的情况下,可以将值传递到此处。

    从版本 1.3.9 新增。

    另请参阅

    create_engine.label_length

  • max_overflow=10 - 允许在连接池 “溢出” 中打开的连接数,即可以超过 pool_size 设置的连接数,后者默认为五。仅在 QueuePool 中使用。

  • module=None – 指向 Python 模块对象的引用(模块本身,而不是其字符串名称)。指定引擎方言要使用的备用 DBAPI 模块。每个子方言引用一个特定的 DBAPI,该 DBAPI 将在第一次连接之前导入。此参数导致跳过导入,并改用给定的模块。不仅可用于 DBAPI 的测试,还可用于将“模拟”DBAPI 实现注入到Engine中。

  • paramstyle=None – 渲染绑定参数时使用的paramstyle。此样式默认为 DBAPI 本身推荐的样式,该样式从 DBAPI 的.paramstyle属性中获取。然而,大多数 DBAPI 都接受多种 paramstyle,特别是将“命名”paramstyle 转换为“位置”paramstyle,或者反之,可能是可取的。当传递此属性时,它应该是以下值之一:"qmark""numeric""named""format""pyformat",并且应该对应于所使用的 DBAPI 已知支持的参数样式。

  • pool=NonePool的一个已构造实例,例如QueuePool实例。如果非 None,则此池将直接用作引擎的底层连接池,跳过 URL 参数中存在的任何连接参数。有关手动构造连接池的信息,请参阅连接池。

  • poolclass=None – 一个Pool子类,将使用 URL 中给定的连接参数来创建连接池实例。请注意,这与pool不同,因为在这种情况下,您实际上并不实例化池,而是指示要使用何种类型的池。

  • pool_logging_name

    将在“sqlalchemy.pool”记录器中生成的日志记录的“name”字段内使用的字符串标识符。默认为对象 id 的十六进制字符串。

    另请参阅

    配置日志记录 - 关于如何配置日志记录的更多详细信息。

  • pool_pre_ping

    如果为 True,则启用连接池“预检”功能,该功能在每次检出时测试连接的活动性。

    自版本 1.2 起新增。

    另请参阅

    断开处理 - 悲观

  • pool_size=5 – 在连接池中保持打开的连接数。与 QueuePool 以及 SingletonThreadPool 一起使用。对于 QueuePoolpool_size 设置为 0 表示没有限制;要禁用池化,请将 poolclass 设置为 NullPool

  • pool_recycle=-1

    此设置会导致池在经过给定的秒数后重新利用连接。默认值为-1,或者没有超时。例如,将其设置为 3600 意味着连接将在一小时后重新利用。请注意,特别是 MySQL 会在连接上检测不到任何活动时自动断开连接,时间为八小时(尽管可以通过 MySQLDB 连接本身和服务器配置进行配置)。

    另请参阅

    设置池回收

  • pool_reset_on_return='rollback'

    设置底层 Pool 对象的 Pool.reset_on_return 参数,该参数可以设置为值 "rollback""commit"None

    另请参阅

    返回时重置

  • pool_timeout=30

    从池中获取连接之前等待的秒数。仅与 QueuePool 一起使用。这可以是一个浮点数,但受 Python 时间函数的限制,可能不可靠在十几毫秒的范围内。

  • pool_use_lifo=False

    当从 QueuePool 检索连接时使用 LIFO(后进先出)而不是 FIFO(先进先出)。使用 LIFO,服务器端的超时方案可以在非高峰使用期间减少使用的连接数。在规划服务器端超时时,请确保使用回收或预先 ping 策略以优雅地处理陈旧的连接。

    版本 1.3 中的新功能。

    另请参阅

    使用 FIFO vs. LIFO

    处理断开连接

  • plugins

    要加载的插件名称的字符串列表。有关背景,请参阅 CreateEnginePlugin

    版本 1.2.3 中的新功能。

  • query_cache_size

    用于缓存查询的 SQL 字符串形式的缓存大小。将其设置为零以禁用缓存。

    当缓存大小达到 N * 1.5 时,最近最少使用的项目将被清理。默认为 500,意味着缓存填满时始终至少存储 500 条 SQL 语句,并且会增长到 750 个项目,然后通过删除最近最少使用的 250 个项目将其减少到 500。

    缓存是在每个语句的基础上完成的,方法是生成一个表示语句结构的缓存键,然后仅当该键不在缓存中时才为当前方言生成字符串 SQL。所有语句都支持缓存,但是一些特性,如具有大量参数的 INSERT,将有意绕过缓存。SQL 日志将指示每个语句的统计信息,无论是否从缓存中拉取。

    注意

    一些与工作单元持久性相关的 ORM 函数以及一些属性加载策略将使用主缓存之外的每个映射器缓存。

    另请参阅

    SQL 编译缓存

    版本 1.4 中的新功能。

  • use_insertmanyvalues

    默认为 True,对于 INSERT..RETURNING 语句,默认使用“insertmanyvalues”执行样式。

    版本 2.0 中的新功能。

    另请参阅

    INSERT 语句的“插入多个值”行为

function sqlalchemy.engine_from_config(configuration: Dict[str, Any], prefix: str = 'sqlalchemy.', **kwargs: Any) → Engine

使用配置字典创建一个新的 Engine 实例。

该字典通常由配置文件生成。

对于 engine_from_config() 感兴趣的键应该有前缀,例如 sqlalchemy.urlsqlalchemy.echo 等。‘prefix’参数指示要搜索的前缀。每个匹配键(前缀被剥离后)都被视为对 create_engine() 调用的相应关键字参数。

唯一必需的键是(假设默认前缀为)sqlalchemy.url,它提供了数据库 URL。

一组选择的关键字参数将根据字符串值“强制转换”为其预期类型。使用 engine_config_types 访问器可针对每个方言扩展参数集。

参数:

  • configuration – 一个字典(通常由配置文件生成,但这不是必需的)。键以“prefix”值开头的项目将删除该前缀,然后传递给 create_engine()

  • prefix – 用于在“configuration”中匹配并剥离键的前缀。

  • kwargs – 传递给 engine_from_config() 的每个关键字参数本身都会覆盖从“configuration”字典中获取的相应项目。关键字参数不应有前缀。

function sqlalchemy.create_mock_engine(url: str | URL, executor: Any, **kw: Any) → MockConnection

创建用于回显 DDL 的“模拟”引擎。

这是一个用于调试或存储由 MetaData.create_all() 和相关方法生成的 DDL 序列输出的实用函数。

该函数接受一个 URL 参数,仅用于确定要使用的方言类型,以及一个“执行器”可调用函数,该函数将接收一个 SQL 表达式对象和参数,然后可以将其回显或以其他方式打印。执行器的返回值不受处理,也不允许调用常规字符串语句,因此仅对发送到数据库的 DDL 有用而不接收任何结果。

例如:

from sqlalchemy import create_mock_engine

def dump(sql, *multiparams, **params):
    print(sql.compile(dialect=engine.dialect))

engine = create_mock_engine('postgresql+psycopg2://', dump)
metadata.create_all(engine, checkfirst=False)

参数:

  • url – 一个字符串 URL,通常只需包含数据库后端名称。

  • executor – 一个可调用的函数,接收参数 sql*multiparams**paramssql 参数通常是 ExecutableDDLElement 的实例,然后可以使用 ExecutableDDLElement.compile() 将其编译为字符串。

从版本 1.4 开始新增:- create_mock_engine() 函数取代了以前与 create_engine() 一起使用的“模拟”引擎策略。

另请参阅

如何将 CREATE TABLE/ DROP TABLE 输出作为字符串获取?

function sqlalchemy.engine.make_url(name_or_url: str | URL) → URL

给定一个字符串,生成一个新的 URL 实例。

URL 的格式通常遵循 RFC-1738,但也有一些例外情况,包括在“scheme”部分接受下划线而不是破折号或句点。

如果传递了 URL 对象,则返回原样。

另请参阅

数据库 URL

function sqlalchemy.create_pool_from_url(url: str | URL, **kwargs: Any) → Pool

从给定的 URL 创建一个池实例。

如果未提供 poolclass 参数,则使用 URL 中指定的方言选择池类。

传递给 create_pool_from_url() 的参数与传递给 create_engine() 函数的池参数相同。

从版本 2.0.10 开始新增。

class sqlalchemy.engine.URL

表示用于连接到数据库的 URL 的各个组件。

URL 通常是从完全格式化的 URL 字符串构建的,其中 make_url() 函数在内部由 create_engine() 函数使用,以将 URL 字符串解析为其各个组件,然后用于构造一个新的 URL 对象。在解析格式化的 URL 字符串时,解析格式通常遵循 RFC-1738,但也有一些例外情况。

一个 URL 对象也可以直接生成,可以使用具有完整形式的 URL 字符串的 make_url() 函数,也可以使用 URL.create() 构造函数以编程方式给出单个字段来构造一个 URL。生成的 URL 对象可以直接传递给 create_engine() 以替换字符串参数的使用,这将在引擎的创建过程中绕过 make_url() 的使用。

从版本 1.4 开始更改:URL 对象现在是一个不可变对象。要创建 URL,请使用 make_url()URL.create() 函数/方法。要修改 URL,请使用诸如 URL.set()URL.update_query_dict() 等方法返回具有修改的新 URL 对象。有关此更改的说明,请参见 URL 对象现在是不可变的。

另请参见

数据库 URL

URL 包含以下属性:

  • URL.drivername: 数据库后端和驱动程序名称,例如postgresql+psycopg2

  • URL.username: 用户名字符串

  • URL.password: 密码字符串

  • URL.host: 主机名字符串

  • URL.port: 整数端口号

  • URL.database: 数据库名称字符串

  • URL.query: 一个表示查询字符串的不可变映射。包含键的字符串和值的字符串或字符串元组。

成员

create(), database, difference_update_query(), drivername, get_backend_name(), get_dialect(), get_driver_name(), host, normalized_query, password, port, query, render_as_string(), set(), translate_connect_args(), update_query_dict(), update_query_pairs(), update_query_string(), username

类签名

sqlalchemy.engine.URL (builtins.tuple)

classmethod create(drivername: str, username: str | None = None, password: str | None = None, host: str | None = None, port: int | None = None, database: str | None = None, query: Mapping[str, Sequence[str] | str] = {}) → URL

创建一个新的URL对象。

另请参阅

数据库 URL

参数:

  • drivername – 数据库后端的名称。该名称将对应于 sqlalchemy/databases 中的一个模块或第三方插件。

  • username – 用户名。

  • password

    数据库密码。通常为字符串,但也可以是可以使用str()进行字符串化的对象。

    注意

    当作为参数传递给URL.create()时,密码字符串不应进行 URL 编码;该字符串应该包含与键入的密码字符完全相同。

    注意

    对于每个Engine对象,一个生成密码的对象只会被字符串化一次。对于每次连接的动态密码生成,请参阅生成动态认证令牌。

  • host – 主机名。

  • port – 端口号。

  • database – 数据库名称。

  • query – 一个字符串键到字符串值的字典,将在连接时传递给方言和/或 DBAPI。要直接指定 Python DBAPI 的非字符串参数,请使用create_engine.connect_args参数来调用create_engine()。另请参阅URL.normalized_query,用于一个一致的字符串->字符串列表字典。

返回:

新的URL对象。

从版本 1.4 开始:URL对象现在是一个不可变的命名元组。此外,query字典也是不可变的。要创建 URL,请使用make_url()URL.create()函数/方法。要修改URL,请使用URL.set()URL.update_query()方法。

attribute database: str | None

数据库名称

method difference_update_query(names: Iterable[str]) → URL

URL.query字典中删除给定的名称,返回新的URL

例如:

url = url.difference_update_query(['foo', 'bar'])

等同于以下使用URL.set()

url = url.set(
    query={
        key: url.query[key]
        for key in set(url.query).difference(['foo', 'bar'])
    }
)

从版本 1.4 开始。

参见

URL.query

URL.update_query_dict()

URL.set()

attribute drivername: str

数据库后端和驱动程序名称,如postgresql+psycopg2

method get_backend_name() → str

返回后端名称。

这是与正在使用的数据库后端相对应的名称,并且是URL.drivername左侧的部分。

method get_dialect(_is_async: bool = False) → Type[Dialect]

返回与此 URL 的驱动程序名称对应的 SQLAlchemy Dialect类。

method get_driver_name() → str

返回后端名称。

这是与正在使用的 DBAPI 驱动程序相对应的名称,并且是URL.drivername右侧的部分。

如果URL.drivername中不包含加号,则导入此URL的默认Dialect以获取驱动程序名称。

attribute host: str | None

主机名或 IP 地址。对于某些驱动程序,也可以是数据源名称。

attribute normalized_query

将值标准化为序列后返回URL.query字典。

由于URL.query字典可能包含字符串值或字符串值序列以区分在查询字符串中多次指定的参数,因此需要通用处理多个参数的代码将希望使用此属性以便将所有出现的参数表示为序列。灵感来自 Python 的urllib.parse.parse_qs函数。例如:

>>> from sqlalchemy.engine import make_url
>>> url = make_url("postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt")
>>> url.query
immutabledict({'alt_host': ('host1', 'host2'), 'ssl_cipher': '/path/to/crt'})
>>> url.normalized_query
immutabledict({'alt_host': ('host1', 'host2'), 'ssl_cipher': ('/path/to/crt',)})
attribute password: str | None

密码,通常是一个字符串,但也可能是具有__str__()方法的任何对象。

attribute port: int | None

整数端口号

attribute query: immutabledict[str, Tuple[str, ...] | str]

代表查询字符串的不可变映射。包含键的字符串和值的字符串或字符串元组,例如:

 >>> from sqlalchemy.engine import make_url
   >>> url = make_url("postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt")
   >>> url.query
   immutabledict({'alt_host': ('host1', 'host2'), 'ssl_cipher': '/path/to/crt'})

To create a mutable copy of this mapping, use the ``dict`` constructor::

   mutable_query_opts = dict(url.query)

另请参阅

URL.normalized_query - 将所有值标准化为序列以进行一致处理

更改URL.query内容的方法:

URL.update_query_dict()

URL.update_query_string()

URL.update_query_pairs()

URL.difference_update_query()

method render_as_string(hide_password: bool = True) → str

将此URL对象呈现为字符串。

当使用__str__()__repr__()方法时使用此方法。该方法直接包括附加选项。

参数:

hide_password – 默认为 True。除非设置为 False,否则密码不会显示在字符串中。

method set(drivername: str | None = None, username: str | None = None, password: str | None = None, host: str | None = None, port: int | None = None, database: str | None = None, query: Mapping[str, Sequence[str] | str] | None = None) → URL

返回一个带有修改的URL对象。

如果值为非 None,则使用该值。要将值显式设置为None,请使用从namedtuple调整的URL._replace()方法。

参数:

  • drivername – 新的驱动程序名称

  • username – 新用户名

  • password – 新密码

  • host – 新主机名

  • port – 新端口

  • query – 新的查询参数,传递一个字符串键的字典,指向字符串或字符串值序列。完全替换了以前的参数列表。

返回:

新的URL对象。

版本 1.4 中的新功能。

另请参阅

URL.update_query_dict()

method translate_connect_args(names: List[str] | None = None, **kw: Any) → Dict[str, Any]

将 url 属性翻译为连接参数的字典。

将此 url 的属性(主机、数据库、用户名、密码、端口)作为普通字典返回。默认情况下,属性名称用作键。未设置或为 false 的属性将从最终字典中省略。

参数:

  • **kw – 可选,url 属性的替代键名。

  • names – 已弃用。与基于关键字的替代名称具有相同目的,但将名称与原始位置相关联。

method update_query_dict(query_parameters: Mapping[str, str | List[str]], append: bool = False) → URL

返回一个由给定字典更新的URL对象的新对象。

字典通常包含字符串键和字符串值。为了表示表达多次的查询参数,传递一个字符串值序列。

例如:

>>> from sqlalchemy.engine import make_url
>>> url = make_url("postgresql+psycopg2://user:pass@host/dbname")
>>> url = url.update_query_dict({"alt_host": ["host1", "host2"], "ssl_cipher": "/path/to/crt"})
>>> str(url)
'postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt'

参数:

  • query_parameters – 具有字符串键和值的字典,值为字符串或字符串序列。

  • append – 如果为 True,则现有查询字符串中的参数不会被删除;新参数将添加到已有参数之后。如果保持其默认值 False,则给定查询参数中的键将替换现有查询字符串中的键。

1.4 版本中的新内容。

另请参阅

URL.query

URL.update_query_string()

URL.update_query_pairs()

URL.difference_update_query()

URL.set()

method update_query_pairs(key_value_pairs: Iterable[Tuple[str, str | List[str]]], append: bool = False) → URL

返回一个新的 URL 对象,其参数字典由给定的键/值对序列更新。

例如:

>>> from sqlalchemy.engine import make_url
>>> url = make_url("postgresql+psycopg2://user:pass@host/dbname")
>>> url = url.update_query_pairs([("alt_host", "host1"), ("alt_host", "host2"), ("ssl_cipher", "/path/to/crt")])
>>> str(url)
'postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt'

参数:

  • key_value_pairs – 包含两个字符串的元组序列。

  • append – 如果为 True,则现有查询字符串中的参数不会被删除;新参数将添加到已有参数之后。如果保持其默认值 False,则给定查询参数中的键将替换现有查询字符串中的键。

1.4 版本中的新内容。

另请参阅

URL.query

URL.difference_update_query()

URL.set()

method update_query_string(query_string: str, append: bool = False) → URL

返回一个新的 URL 对象,其参数字典由给定的查询字符串更新。

例如:

>>> from sqlalchemy.engine import make_url
>>> url = make_url("postgresql+psycopg2://user:pass@host/dbname")
>>> url = url.update_query_string("alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt")
>>> str(url)
'postgresql+psycopg2://user:pass@host/dbname?alt_host=host1&alt_host=host2&ssl_cipher=%2Fpath%2Fto%2Fcrt'

参数:

  • query_string – 一个 URL 转义的查询字符串,不包括问号。

  • append – 如果为 True,则现有查询字符串中的参数不会被删除;新参数将添加到已有参数之后。如果保持其默认值 False,则给定查询参数中的键将替换现有查询字符串中的键。

1.4 版本中的新内容。

另请参阅

URL.query

URL.update_query_dict()

attribute username: str | None

用户名 字符串

池化

当调用 connect()execute() 方法时,Engine 将向连接池请求连接。默认连接池 QueuePool 将根据需要打开与数据库的连接。随着并发语句的执行,QueuePool 将增加其连接池的大小至默认大小为五,并允许默认的“溢出”为十。由于 Engine 本质上是连接池的“基地”,因此在应用程序中,应该保持单个 Engine 与数据库建立连接,而不是为每个连接创建新的。

注意

默认情况下,SQLite 引擎不使用 QueuePool。有关 SQLite 连接池使用的详细信息,请参阅 SQLite。

欲了解更多有关连接池的信息,请参阅连接池。

自定义 DBAPI connect() 参数 / 连接时例程

对于需要特殊连接方法的情况,在绝大多数情况下,最适合的方法是使用 create_engine() 层级中的一些钩子来自定义此过程。这些在以下子章节中描述。

传递给 dbapi.connect() 的特殊关键字参数

所有 Python DBAPI 都接受除连接基础之外的附加参数。常见的参数包括用于指定字符集编码和超时值的参数;更复杂的数据包括特殊的 DBAPI 常量和对象以及 SSL 子参数。有两种基本的方式可以在不复杂的情况下传递这些参数。

将参数添加到 URL 查询字符串

简单的字符串值,以及一些数值和布尔标志,可以直接在 URL 的查询字符串中指定。其中一个常见的例子是接受字符编码参数 encoding 的 DBAPI,例如大多数 MySQL DBAPI:

engine = create_engine("mysql+pymysql://user:pass@host/test?charset=utf8mb4")

使用查询字符串的优点是可以在配置文件中指定附加的 DBAPI 选项,以便与 URL 中指定的 DBAPI 兼容。在这个级别传递的具体参数因 SQLAlchemy 方言而异。一些方言将所有参数都作为字符串传递,而另一些方言将解析特定的数据类型并将参数移到不同的位置,比如驱动程序级别的 DSN 和连接字符串。由于此领域的每个方言的行为当前存在差异,因此应该查阅用于特定方言的方言文档,以查看在此级别是否支持特定参数。

提示

使用 Dialect.create_connect_args() 方法可以执行以下操作,显示给定 URL 的 DBAPI 传递的确切参数:

>>> from sqlalchemy import create_engine
>>> engine = create_engine(
...     "mysql+pymysql://some_user:some_pass@some_host/test?charset=utf8mb4"
... )
>>> args, kwargs = engine.dialect.create_connect_args(engine.url)
>>> args, kwargs
([], {'host': 'some_host', 'database': 'test', 'user': 'some_user', 'password': 'some_pass', 'charset': 'utf8mb4', 'client_flag': 2})

上述 args, kwargs 对通常作为 dbapi.connect(*args, **kwargs) 传递给 DBAPI。

使用 connect_args 字典参数

一个更一般的系统,用于将任何参数传递给 dbapi.connect() 函数,保证始终传递所有参数,是 create_engine.connect_args 字典参数。这可用于否则未在查询字符串中处理的参数,以及在必须将特殊子结构或对象传递给 DBAPI 时。有时,只需将特定标志作为 True 符号发送,而 SQLAlchemy 方言并不知道如何从 URL 中的字符串形式强制转换此关键字参数。下面示例说明了使用 psycopg2 “连接工厂” 替换连接的基础实现:

engine = create_engine(
    "postgresql+psycopg2://user:pass@hostname/dbname",
    connect_args={"connection_factory": MyConnectionFactory},
)

另一个示例是 pyodbc 的 “timeout” 参数:

engine = create_engine(
    "mssql+pyodbc://user:pass@sqlsrvr?driver=ODBC+Driver+13+for+SQL+Server",
    connect_args={"timeout": 30},
)

上述示例还说明了 URL “查询字符串”参数以及 create_engine.connect_args 可以同时使用;在 pyodbc 的情况下,“driver” 关键字在 URL 中具有特殊含义。

控制参数如何传递给 DBAPI 的 connect() 函数

除了操作传递给 connect() 的参数外,我们还可以使用 DialectEvents.do_connect() 事件挂钩进一步定制 DBAPI connect() 函数本身的调用方式。此挂钩传递了方言将发送给 connect() 的完整 *args, **kwargs。然后,这些集合可以就地修改以更改它们的使用方式:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "do_connect")
def receive_do_connect(dialect, conn_rec, cargs, cparams):
    cparams["connection_factory"] = MyConnectionFactory

生成动态认证令牌

DialectEvents.do_connect() 也是动态插入认证令牌的理想方式,该令牌可能在 Engine 的生命周期内发生变化。例如,如果令牌由 get_authentication_token() 生成并在 token 参数中传递给 DBAPI,则可以实现如下:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user@hostname/dbname")

@event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    cparams["token"] = get_authentication_token()

另请参阅

使用访问令牌连接数据库 - 涉及 SQL Server 的更具体示例

修改连接后的 DBAPI 连接,或在连接后运行命令

对于 SQLAlchemy 创建的 DBAPI 连接,如果我们想在实际使用之前修改已完成的连接,比如设置特殊标志或运行某些命令,PoolEvents.connect()事件钩子是最合适的钩子。这个钩子在每个新连接创建时调用,然后被 SQLAlchemy 使用:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "connect")
def connect(dbapi_connection, connection_record):
    cursor_obj = dbapi_connection.cursor()
    cursor_obj.execute("SET some session variables")
    cursor_obj.close()

完全替换 DBAPI 的connect()函数

最后,DialectEvents.do_connect()事件钩子也可以让我们完全接管连接过程,建立连接并返回它:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "do_connect")
def receive_do_connect(dialect, conn_rec, cargs, cparams):
    # return the new DBAPI connection with whatever we'd like to
    # do
    return psycopg2.connect(*cargs, **cparams)

DialectEvents.do_connect()钩子取代了以前的create_engine.creator钩子,该钩子仍然可用。DialectEvents.do_connect()具有一个明显的优势,即从 URL 解析的完整参数也传递给用户定义的函数,而这在create_engine.creator中并非如此。

传递给dbapi.connect()的特殊关键字参数

所有 Python DBAPI 都接受除了基本连接之外的额外参数。常见参数包括用于指定字符集编码和超时值的参数;更复杂的数据包括特殊的 DBAPI 常量和对象以及 SSL 子参数。有两种基本的方法可以传递这些参数而不增加复杂性。

将参数添加到 URL 查询字符串中

简单的字符串值,以及一些数值和布尔标志,通常可以直接在 URL 的查询字符串中指定。一个常见的例子是接受encoding参数用于字符编码的 DBAPIs,比如大多数 MySQL DBAPIs:

engine = create_engine("mysql+pymysql://user:pass@host/test?charset=utf8mb4")

使用查询字符串的优势在于可以在配置文件中指定额外的 DBAPI 选项,以一种对 URL 中指定的 DBAPI 可移植的方式。在这个级别传递的具体参数因 SQLAlchemy 方言而异。一些方言将所有参数都作为字符串传递,而其他方言将解析特定数据类型并将参数移动到不同位置,比如驱动程序级别的 DSN 和连接字符串。由于当前在这个领域的方言行为各不相同,应该查阅特定方言的文档,以查看在这个级别是否支持特定参数。

提示

一种显示给定 URL 的 DBAPI 所传递的精确参数的一般技术可以直接使用 Dialect.create_connect_args() 方法,如下所示:

>>> from sqlalchemy import create_engine
>>> engine = create_engine(
...     "mysql+pymysql://some_user:some_pass@some_host/test?charset=utf8mb4"
... )
>>> args, kwargs = engine.dialect.create_connect_args(engine.url)
>>> args, kwargs
([], {'host': 'some_host', 'database': 'test', 'user': 'some_user', 'password': 'some_pass', 'charset': 'utf8mb4', 'client_flag': 2})

上述 args, kwargs 对通常作为 dbapi.connect(*args, **kwargs) 传递给 DBAPI。

使用 connect_args 字典参数

一种更通用的系统,用于向 dbapi.connect() 函数传递任何参数,并保证始终传递所有参数,是 create_engine.connect_args 字典参数。这可用于那些否则不被方言处理的参数,当它们添加到查询字符串时,以及当必须将特殊子结构或对象传递给 DBAPI 时。有时,只需将特定标志发送为 True 符号,而 SQLAlchemy 方言不知道此关键字参数以将其从其 URL 中呈现的字符串形式强制转换。下面说明了使用 psycopg2 “连接工厂” 的示例,用它替换了连接的底层实现:

engine = create_engine(
    "postgresql+psycopg2://user:pass@hostname/dbname",
    connect_args={"connection_factory": MyConnectionFactory},
)

另一个示例是 pyodbc 的 “timeout” 参数:

engine = create_engine(
    "mssql+pyodbc://user:pass@sqlsrvr?driver=ODBC+Driver+13+for+SQL+Server",
    connect_args={"timeout": 30},
)

上面的示例还说明了 URL “查询字符串”参数以及 create_engine.connect_args 可以同时使用;在 pyodbc 的情况下,“driver” 关键字在 URL 中具有特殊意义。

向 URL 查询字符串添加参数

简单的字符串值,以及一些数值和布尔标志,通常可以直接在 URL 的查询字符串中指定。这种情况的常见例子是接受字符编码参数 encoding 的 DBAPI,例如大多数 MySQL DBAPI:

engine = create_engine("mysql+pymysql://user:pass@host/test?charset=utf8mb4")

使用查询字符串的优点是可以在配置文件中指定其他 DBAPI 选项,这种方式对于在 URL 中指定的 DBAPI 是可移植的。通过此级别传递的特定参数因 SQLAlchemy 方言而异。一些方言将所有参数作为字符串传递,而其他方言将解析特定的数据类型并将参数移到不同的位置,例如到驱动程序级别的 DSN 和连接字符串。由于此领域的方言行为目前有所变化,因此应查阅使用的特定方言的方言文档,以查看在此级别是否支持特定参数。

提示

一种显示给定 URL 的 DBAPI 所传递的精确参数的一般技术可以直接使用 Dialect.create_connect_args() 方法,如下所示:

>>> from sqlalchemy import create_engine
>>> engine = create_engine(
...     "mysql+pymysql://some_user:some_pass@some_host/test?charset=utf8mb4"
... )
>>> args, kwargs = engine.dialect.create_connect_args(engine.url)
>>> args, kwargs
([], {'host': 'some_host', 'database': 'test', 'user': 'some_user', 'password': 'some_pass', 'charset': 'utf8mb4', 'client_flag': 2})

上述 args, kwargs 对通常作为 dbapi.connect(*args, **kwargs) 传递给 DBAPI。

使用 connect_args 字典参数

将任何参数传递给dbapi.connect()函数的更通用的系统,保证始终传递所有参数的是create_engine.connect_args字典参数。这可以用于否则不会由方言处理的添加到查询字符串的参数,以及当必须将特殊子结构或对象传递给 DBAPI 时。有时只是一个特定标志必须作为True符号发送,并且 SQLAlchemy 方言不知道此关键字参数以将其从 URL 中呈现的字符串形式强制转换为其。下面说明了使用 psycopg2 的“connection factory”替换连接的底层实现的示例:

engine = create_engine(
    "postgresql+psycopg2://user:pass@hostname/dbname",
    connect_args={"connection_factory": MyConnectionFactory},
)

另一个例子是 pyodbc 的“timeout”参数:

engine = create_engine(
    "mssql+pyodbc://user:pass@sqlsrvr?driver=ODBC+Driver+13+for+SQL+Server",
    connect_args={"timeout": 30},
)

上述示例还说明了 URL“查询字符串”参数以及create_engine.connect_args都可以同时使用;在 pyodbc 的情况下,“driver”关键字在 URL 中具有特殊含义。

控制参数传递到 DBAPI connect() 函数的方式

除了操纵传递给 connect() 的参数之外,我们还可以通过DialectEvents.do_connect()事件钩子进一步自定义 DBAPI connect() 函数本身的调用方式。此钩子传递了方言将发送给 connect() 的完整的 *args, **kwargs。然后可以修改这些集合以更改它们的使用方式:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "do_connect")
def receive_do_connect(dialect, conn_rec, cargs, cparams):
    cparams["connection_factory"] = MyConnectionFactory

生成动态认证令牌

DialectEvents.do_connect()也是一种理想的方法,可以动态插入可能会随着Engine寿命而变化的认证令牌。例如,如果令牌由get_authentication_token()生成并在token参数中传递给 DBAPI,则可以实现为:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user@hostname/dbname")

@event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    cparams["token"] = get_authentication_token()

请参阅

使用访问令牌连接数据库 - 一个更具体的例子涉及 SQL Server #### 生成动态认证令牌

DialectEvents.do_connect()也是一种理想的方法,可以动态插入可能会随着Engine寿命而变化的认证令牌。例如,如果令牌由get_authentication_token()生成并在token参数中传递给 DBAPI,则可以实现为:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user@hostname/dbname")

@event.listens_for(engine, "do_connect")
def provide_token(dialect, conn_rec, cargs, cparams):
    cparams["token"] = get_authentication_token()

请参阅

使用访问令牌连接数据库 - 一个更具体的涉及 SQL Server 的例子

在连接后修改 DBAPI 连接,或在连接后运行命令

对于 SQLAlchemy 创建的 DBAPI 连接,虽然没有问题,但我们想要在实际使用之前修改完成的连接的情况,例如设置特殊标志或运行某些命令,PoolEvents.connect()事件钩子是最合适的钩子。这个钩子在每次创建新连接时调用,然后由 SQLAlchemy 使用:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "connect")
def connect(dbapi_connection, connection_record):
    cursor_obj = dbapi_connection.cursor()
    cursor_obj.execute("SET some session variables")
    cursor_obj.close()

完全替换 DBAPI 的connect()函数

最后,DialectEvents.do_connect()事件钩子也可以允许我们完全接管连接过程,建立连接并返回它:

from sqlalchemy import event

engine = create_engine("postgresql+psycopg2://user:pass@hostname/dbname")

@event.listens_for(engine, "do_connect")
def receive_do_connect(dialect, conn_rec, cargs, cparams):
    # return the new DBAPI connection with whatever we'd like to
    # do
    return psycopg2.connect(*cargs, **cparams)

DialectEvents.do_connect()钩子取代了以前的create_engine.creator钩子,后者仍然可用。DialectEvents.do_connect()的明显优势是解析自 URL 的完整参数也传递给用户定义的函数,而对于create_engine.creator不是这样。

配置日志记录

Python 的标准logging模块用于实现 SQLAlchemy 的信息和调试日志输出。这使得 SQLAlchemy 的日志记录可以以标准方式与其他应用程序和库集成。create_engine()上还有两个参数create_engine.echocreate_engine.echo_pool,它们允许立即将日志记录到sys.stdout以进行本地开发;这些参数最终与下面描述的常规 Python 记录器进行交互。

这一节假设您熟悉上面链接的日志记录模块。SQLAlchemy 执行的所有日志记录都存在于sqlalchemy命名空间下,就像logging.getLogger('sqlalchemy')所使用的那样。当日志记录已经配置好(例如通过logging.basicConfig()),可以打开的 SA 日志记录器的一般命名空间如下所示:

  • sqlalchemy.engine - 控制 SQL 回显。设置为logging.INFO以输出 SQL 查询结果,设置为logging.DEBUG以输出查询结果集。这些设置相当于create_engine.echo 上的echo=Trueecho="debug"

  • sqlalchemy.pool - 控制连接池日志记录。设置为logging.INFO以记录连接失效和回收事件;设置为logging.DEBUG以额外记录所有连接池的签入和签出。这些设置相当于create_engine.echo_pool 上的pool_echo=Truepool_echo="debug"

  • sqlalchemy.dialects - 控制 SQL 方言的自定义日志记录,以特定方言内部使用日志的程度为准,通常很少。

  • sqlalchemy.orm - 控制各种 ORM 函数的日志记录,以 ORM 内部使用日志的程度为准,通常很少。设置为logging.INFO以记录一些有关映射器配置的顶级信息。

例如,要使用 Python 日志记录而不是echo=True标志记录 SQL 查询:

import logging

logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)

默认情况下,整个 sqlalchemy 命名空间的日志级别设置为logging.WARN,因此即使在已启用日志记录的应用程序中也不会发生任何日志操作。

注意

SQLAlchemy Engine 通过仅在检测到当前日志级别为logging.INFOlogging.DEBUG时发出日志语句来节省 Python 函数调用开销。仅在从连接池获取新连接时才检查此级别。因此,在更改已运行应用程序的日志配置时,任何当前活动的 Connection(更常见的是处于事务中的活动 Session 对象)都不会根据新配置记录任何 SQL,直到获取新的 Connection(在 Session 的情况下,这是在当前事务结束并开始新事务之后)。

更多关于 Echo 标志

如前所述,create_engine.echocreate_engine.echo_pool 参数是直接将日志记录到 sys.stdout 的快捷方式:

>>> from sqlalchemy import create_engine, text
>>> e = create_engine("sqlite://", echo=True, echo_pool="debug")
>>> with e.connect() as conn:
...     print(conn.scalar(text("select 'hi'")))
2020-10-24 12:54:57,701 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Created new connection <sqlite3.Connection object at 0x7f287819ac60>
2020-10-24 12:54:57,701 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> checked out from pool
2020-10-24 12:54:57,702 INFO sqlalchemy.engine.Engine select 'hi'
2020-10-24 12:54:57,702 INFO sqlalchemy.engine.Engine ()
hi
2020-10-24 12:54:57,703 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> being returned to pool
2020-10-24 12:54:57,704 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> rollback-on-return

使用这些标志大致相当于:

import logging

logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
logging.getLogger("sqlalchemy.pool").setLevel(logging.DEBUG)

需要注意的是,这两个标志独立于任何现有的日志配置工作,并且将无条件地使用 logging.basicConfig()。这将在除了任何现有记录器配置之外额外配置。因此,在显式配置日志时,请确保始终将所有回显标志设置为 False,以避免获取重复的日志行。

设置日志名称

诸如 EnginePool 的实例的记录器名称默认为使用截断的十六进制标识符字符串。要将其设置为特定名称,请使用 create_engine.logging_namecreate_engine.pool_logging_namesqlalchemy.create_engine();名称将附加到日志名称 sqlalchemy.engine.Engine

>>> import logging
>>> from sqlalchemy import create_engine
>>> from sqlalchemy import text
>>> logging.basicConfig()
>>> logging.getLogger("sqlalchemy.engine.Engine.myengine").setLevel(logging.INFO)
>>> e = create_engine("sqlite://", logging_name="myengine")
>>> with e.connect() as conn:
...     conn.execute(text("select 'hi'"))
2020-10-24 12:47:04,291 INFO sqlalchemy.engine.Engine.myengine select 'hi'
2020-10-24 12:47:04,292 INFO sqlalchemy.engine.Engine.myengine ()

提示

create_engine.logging_namecreate_engine.pool_logging_name 参数也可与 create_engine.echocreate_engine.echo_pool 结合使用。然而,如果其他引擎创建时将回显标志设置为 True 而没有日志名称,则将会发生无法避免的双重记录条件。这是因为将自动为 sqlalchemy.engine.Engine 添加处理程序,它将同时记录无名称的引擎和具有日志名称的引擎的消息。例如:

from sqlalchemy import create_engine, text

e1 = create_engine("sqlite://", echo=True, logging_name="myname")
with e1.begin() as conn:
    conn.execute(text("SELECT 1"))

e2 = create_engine("sqlite://", echo=True)
with e2.begin() as conn:
    conn.execute(text("SELECT 2"))

with e1.begin() as conn:
    conn.execute(text("SELECT 3"))

上述情况将会双重记录 SELECT 3。为解决此问题,请确保所有引擎都设置了 logging_name,或者使用显式的记录器/处理器设置,而不使用create_engine.echocreate_engine.echo_pool

设置每个连接/子引擎令牌

自 1.4.0b2 版本新增。

虽然在长期存在的Engine对象上建立日志名称是适当的,但它不够灵活,无法容纳任意长的名称列表,以跟踪日志消息中的单个连接和/或事务的情况。

对于此用例,由 ConnectionResult 对象生成的日志消息本身可以通过附加诸如事务或请求标识符之类的其他标记来增强。 Connection.execution_options.logging_token 参数接受一个字符串参数,该参数可用于建立每个连接的跟踪标记:

>>> from sqlalchemy import create_engine
>>> e = create_engine("sqlite://", echo="debug")
>>> with e.connect().execution_options(logging_token="track1") as conn:
...     conn.execute(text("select 1")).all()
2021-02-03 11:48:45,754 INFO sqlalchemy.engine.Engine [track1] select 1
2021-02-03 11:48:45,754 INFO sqlalchemy.engine.Engine [track1] [raw sql] ()
2021-02-03 11:48:45,754 DEBUG sqlalchemy.engine.Engine [track1] Col ('1',)
2021-02-03 11:48:45,755 DEBUG sqlalchemy.engine.Engine [track1] Row (1,)

Connection.execution_options.logging_token 参数也可以通过 create_engine.execution_optionsEngine.execution_options() 在引擎或子引擎上建立。这可能对将不同的日志标记应用于应用程序的不同组件而不创建新引擎非常有用:

>>> from sqlalchemy import create_engine
>>> e = create_engine("sqlite://", echo="debug")
>>> e1 = e.execution_options(logging_token="track1")
>>> e2 = e.execution_options(logging_token="track2")
>>> with e1.connect() as conn:
...     conn.execute(text("select 1")).all()
2021-02-03 11:51:08,960 INFO sqlalchemy.engine.Engine [track1] select 1
2021-02-03 11:51:08,960 INFO sqlalchemy.engine.Engine [track1] [raw sql] ()
2021-02-03 11:51:08,960 DEBUG sqlalchemy.engine.Engine [track1] Col ('1',)
2021-02-03 11:51:08,961 DEBUG sqlalchemy.engine.Engine [track1] Row (1,)

>>> with e2.connect() as conn:
...     conn.execute(text("select 2")).all()
2021-02-03 11:52:05,518 INFO sqlalchemy.engine.Engine [track2] Select 1
2021-02-03 11:52:05,519 INFO sqlalchemy.engine.Engine [track2] [raw sql] ()
2021-02-03 11:52:05,520 DEBUG sqlalchemy.engine.Engine [track2] Col ('1',)
2021-02-03 11:52:05,520 DEBUG sqlalchemy.engine.Engine [track2] Row (1,)

隐藏参数

Engine 发出的日志还显示了特定语句中存在的 SQL 参数摘录。为了防止出于隐私目的记录这些参数,请启用 create_engine.hide_parameters 标志:

>>> e = create_engine("sqlite://", echo=True, hide_parameters=True)
>>> with e.connect() as conn:
...     conn.execute(text("select :some_private_name"), {"some_private_name": "pii"})
2020-10-24 12:48:32,808 INFO sqlalchemy.engine.Engine select ?
2020-10-24 12:48:32,808 INFO sqlalchemy.engine.Engine [SQL parameters hidden due to hide_parameters=True]

回声标记的更多信息

正如前面提到的,create_engine.echocreate_engine.echo_pool 参数是立即记录到 sys.stdout 的快捷方式:

>>> from sqlalchemy import create_engine, text
>>> e = create_engine("sqlite://", echo=True, echo_pool="debug")
>>> with e.connect() as conn:
...     print(conn.scalar(text("select 'hi'")))
2020-10-24 12:54:57,701 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Created new connection <sqlite3.Connection object at 0x7f287819ac60>
2020-10-24 12:54:57,701 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> checked out from pool
2020-10-24 12:54:57,702 INFO sqlalchemy.engine.Engine select 'hi'
2020-10-24 12:54:57,702 INFO sqlalchemy.engine.Engine ()
hi
2020-10-24 12:54:57,703 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> being returned to pool
2020-10-24 12:54:57,704 DEBUG sqlalchemy.pool.impl.SingletonThreadPool Connection <sqlite3.Connection object at 0x7f287819ac60> rollback-on-return

使用这些标志大致相当于:

import logging

logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
logging.getLogger("sqlalchemy.pool").setLevel(logging.DEBUG)

需要注意的是,这两个标志独立于任何现有的日志配置工作,并且无条件使用 logging.basicConfig()。这会使其效果叠加到任何现有的记录器配置中。因此,在明确配置日志时,请始终确保所有回声标志都设置为 False,以避免获得重复的日志行。

设置日志名称

实例的记录器名称(如EnginePool)默认为使用截断的十六进制标识符字符串。要将其设置为特定名称,请使用create_engine.logging_namecreate_engine.pool_logging_namesqlalchemy.create_engine();该名称将附加到日志名称sqlalchemy.engine.Engine

>>> import logging
>>> from sqlalchemy import create_engine
>>> from sqlalchemy import text
>>> logging.basicConfig()
>>> logging.getLogger("sqlalchemy.engine.Engine.myengine").setLevel(logging.INFO)
>>> e = create_engine("sqlite://", logging_name="myengine")
>>> with e.connect() as conn:
...     conn.execute(text("select 'hi'"))
2020-10-24 12:47:04,291 INFO sqlalchemy.engine.Engine.myengine select 'hi'
2020-10-24 12:47:04,292 INFO sqlalchemy.engine.Engine.myengine ()

小贴士

create_engine.logging_namecreate_engine.pool_logging_name参数也可以与create_engine.echocreate_engine.echo_pool一起使用。但是,如果其他引擎设置了 echo 标志为 True 而没有设置日志名称,则将会发生不可避免的双重记录条件。这是因为将自动为sqlalchemy.engine.Engine添加处理程序,该处理程序将同时为无名称的引擎和具有日志名称的引擎记录消息。例如:

from sqlalchemy import create_engine, text

e1 = create_engine("sqlite://", echo=True, logging_name="myname")
with e1.begin() as conn:
    conn.execute(text("SELECT 1"))

e2 = create_engine("sqlite://", echo=True)
with e2.begin() as conn:
    conn.execute(text("SELECT 2"))

with e1.begin() as conn:
    conn.execute(text("SELECT 3"))

上述情景将对SELECT 3进行双重记录。为了解决此问题,请确保所有引擎都设置了logging_name,或者在不使用create_engine.echocreate_engine.echo_pool的情况下,使用显式的记录器/处理程序设置。

设置每个连接/子引擎令牌

1.4.0b2 版本中的新功能。

虽然在长期存在的Engine对象上建立记录名称是合适的,但是对于跟踪日志消息中的单个连接和/或事务的情况,它的灵活性不够。

对于这种用例,由ConnectionResult对象生成的日志消息本身可能会使用附加令牌(例如事务或请求标识符)进行扩充。Connection.execution_options.logging_token参数接受一个字符串参数,该参数可用于建立每个连接的跟踪令牌:

>>> from sqlalchemy import create_engine
>>> e = create_engine("sqlite://", echo="debug")
>>> with e.connect().execution_options(logging_token="track1") as conn:
...     conn.execute(text("select 1")).all()
2021-02-03 11:48:45,754 INFO sqlalchemy.engine.Engine [track1] select 1
2021-02-03 11:48:45,754 INFO sqlalchemy.engine.Engine [track1] [raw sql] ()
2021-02-03 11:48:45,754 DEBUG sqlalchemy.engine.Engine [track1] Col ('1',)
2021-02-03 11:48:45,755 DEBUG sqlalchemy.engine.Engine [track1] Row (1,)

Connection.execution_options.logging_token 参数也可以通过 create_engine.execution_optionsEngine.execution_options() 在引擎或子引擎上建立。这可能对于在不创建新引擎的情况下将不同的日志令牌应用于应用程序的不同组件很有用:

>>> from sqlalchemy import create_engine
>>> e = create_engine("sqlite://", echo="debug")
>>> e1 = e.execution_options(logging_token="track1")
>>> e2 = e.execution_options(logging_token="track2")
>>> with e1.connect() as conn:
...     conn.execute(text("select 1")).all()
2021-02-03 11:51:08,960 INFO sqlalchemy.engine.Engine [track1] select 1
2021-02-03 11:51:08,960 INFO sqlalchemy.engine.Engine [track1] [raw sql] ()
2021-02-03 11:51:08,960 DEBUG sqlalchemy.engine.Engine [track1] Col ('1',)
2021-02-03 11:51:08,961 DEBUG sqlalchemy.engine.Engine [track1] Row (1,)

>>> with e2.connect() as conn:
...     conn.execute(text("select 2")).all()
2021-02-03 11:52:05,518 INFO sqlalchemy.engine.Engine [track2] Select 1
2021-02-03 11:52:05,519 INFO sqlalchemy.engine.Engine [track2] [raw sql] ()
2021-02-03 11:52:05,520 DEBUG sqlalchemy.engine.Engine [track2] Col ('1',)
2021-02-03 11:52:05,520 DEBUG sqlalchemy.engine.Engine [track2] Row (1,)

隐藏参数

Engine 发出的日志还指示了对于特定语句存在的 SQL 参数的摘录。为了防止出于隐私目的记录这些参数,请启用 create_engine.hide_parameters 标志:

>>> e = create_engine("sqlite://", echo=True, hide_parameters=True)
>>> with e.connect() as conn:
...     conn.execute(text("select :some_private_name"), {"some_private_name": "pii"})
2020-10-24 12:48:32,808 INFO sqlalchemy.engine.Engine select ?
2020-10-24 12:48:32,808 INFO sqlalchemy.engine.Engine [SQL parameters hidden due to hide_parameters=True]
posted @ 2024-06-22 11:33  绝不原创的飞龙  阅读(12)  评论(0编辑  收藏  举报