版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

自定义类型

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

重写类型编译

经常需要强制更改类型的“字符串”版本,即在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"

上面的代码允许使用types.BINARY,它将产生字符串BINARY,用于除SQLite以外的所有后端,在这种情况下,它将产生BLOB

有关其他示例,请参阅Changing Compilation of Types一节(Custom SQL Constructs and Compilation Extension)的小节。

增加现有类型

The TypeDecorator allows the creation of custom types which add bind-parameter and result-processing behavior to an existing type object. 当需要对数据库进行额外的Python数据封送处理时使用。

注意

The bind- and result-processing of TypeDecorator is in addition to the processing already performed by the hosted type, which is customized by SQLAlchemy on a per-DBAPI basis to perform processing specific to that DBAPI. 要更改现有类型的DBAPI级处理,请参阅Replacing the Bind/Result Processing of Existing Types部分。

class sqlalchemy.types.TypeDecorator(*args, **kwargs)

基础:sqlalchemy.sql.expression.SchemaEventTargetsqlalchemy.types.TypeEngine

允许创建向现有类型添加附加功能的类型。

此方法优先于指定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

    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作为占位符。

接收与所使用的最终类型不相似的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

    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)

Our expression of somecol + date with the above type will coerce the “date” on the right side to also be treated as MyEpochType.

此行为可以通过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扩大了某些类型运算符需要特殊逻辑的类型,则必须重写此方法一个关键的例子是装饰postgresql.JSONpostgresql.JSONB类型;应该使用TypeEngine.coerce_compared_value()的默认规则来处理像索引操作这样的操作符:

class MyJsonType(TypeDecorator):
    impl = postgresql.JSON

    def coerce_compared_value(self, op, value):
        return self.impl.coerce_compared_value(op, value)

如果没有上述步骤,像mycol['foo']这样的索引操作将导致索引值'foo'被JSON编码。

__ init __ * args** kwargs T5>

构建一个TypeDecorator

Arguments sent here are passed to the constructor of the class assigned to the impl class level attribute, assuming the impl is a callable, and the resulting object is assigned to the self.impl instance attribute (thus overriding the class attribute of the same name).

如果类级别impl不是一个可调用的(不寻常的情况),它将被赋予相同的实例属性“原样”,忽略传递给构造函数的那些参数。

子类可以覆盖它来完全自定义self.impl的生成。

adapt(cls, **kw)
inherited from the adapt() method of TypeEngine

产生这种类型的“适应”形式,给予一个“impl”类来处理。

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

bind_expression T0> ( T1> bindvalue T2> ) T3> ¶ T4>
inherited from the bind_expression() method of TypeEngine

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

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

该方法在语句编译时进行评估,而不是语句构建时间。

注意,这个方法在实现的时候,应该总是返回完全相同的结构,没有任何条件逻辑,因为它可以用于针对任意数量的绑定参数集的executemany()调用。

也可以看看:

Applying SQL-level Bind/Result Processing

bind_processor T0> ( T1> 方言 T2> ) T3> ¶ T4>

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

这是实现绑定值转换的TypeEngine合同的方法。TypeDecorator will wrap a user-defined implementation of process_bind_param() here.

用户定义的代码可以直接覆盖这个方法,尽管最好使用process_bind_param(),以保持self.impl提供的处理。

参数:dialect – Dialect instance in use.

此方法与此类的result_processor()方法相反。

coerce_compared_value opvalue

在表达式中建议一个“被强制的”Python值的类型。

默认情况下,返回自我。当使用这种类型的对象位于表达式的左侧或右侧,而不是指定了SQLAlchemy类型的纯Python对象时,表达式系统将调用此方法:

expr = table.c.somecolumn + 35

Where above, if somecolumn uses this type, this method will be called with the value operator.add and 35. 返回值是任何SQLAlchemy类型应该用于35这个特定的操作。

coerce_to_is_types =(< type'NoneType>,)

当使用==进行比较时,将表达式级别应强制的Python类型指定为“IS ”(对于IS !=结合

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

TypeDecorator modifies this list to only include NoneType, as typedecorator implementations that deal with boolean types are common.

Custom TypeDecorator类可以重写此属性以返回空元组,在这种情况下,不会将值强制为常量。

..versionadded :: 0.8.2
添加了TypeDecorator.coerce_to_is_types,以便更容易地控制__eq__() __ne__()操作。
column_expression T0> ( T1> colexpr T2> ) T3> ¶ T4>
inherited from the column_expression() method of TypeEngine

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

这通常是一个SQL函数,它包装一个列表达式,并在SELECT语句的columns子句中呈现。它被用于特殊的数据类型,这些特殊的数据类型需要将列包装在一些特殊的数据库函数中,以便在将值发送回应用程序之前强制该值。它是TypeEngine.result_processor()方法的SQL模拟。

该方法在语句编译时进行评估,而不是语句构建时间。

也可以看看:

Applying SQL-level Bind/Result Processing

compare_against_backend dialectconn_type

将此类型与给定的后端类型进行比较。

这个函数目前还没有为SQLAlchemy类型实现,所有内置的类型都会返回None但是,它可以通过用户定义的类型实现,可以通过模式比较工具(如Alembic autogenerate)使用它。

未来的SQLAlchemy版本也可能会阻碍这种内置类型的方法。

如果此类型与给定类型相同,则该函数应返回True;该类型通常反映在数据库中,因此应该是数据库特定的。使用的方言也通过了。它也可以返回False来声明该类型不相同。

参数:
  • dialect – a Dialect that is involved in the comparison.
  • conn_type - 从后端反射的类型对象。

新版本1.0.3.

比较值 xy

给定两个值,比较他们的平等。

By default this calls upon TypeEngine.compare_values() of the underlying “impl”, which in turn usually uses the Python equals operator ==.

ORM使用该函数将原始加载值与截获的“已更改”值进行比较,以确定是否发生净变化。

编译 T0> ( T1> 方言=无 T2> ) T3> ¶ T4>
inherited from the compile() method of TypeEngine

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

当没有参数调用时,使用“默认”方言产生一个字符串结果。

参数:dialect – a Dialect instance.
复制 T0> ( T1> **千瓦 T2> ) T3> ¶ T4>

生成这个TypeDecorator实例的副本。

这是一个浅的副本,并提供来履行TypeEngine合约的一部分。通常不需要重写,除非用户定义的TypeDecorator具有应该被深度复制的本地状态。

dialect_impl T0> ( T1> 方言 T2> ) T3> ¶ T4>
inherited from the dialect_impl() method of TypeEngine

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

evaluates_none T0> ( T1> ) T2> ¶ T3>
inherited from the evaluates_none() method of TypeEngine

返回具有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类型,它可能希望保持JSON值'null'

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

版本1.1中的新功能

也可以看看

Forcing NULL on a column with a default - in the ORM documentation

postgresql.JSON.none_as_null - Postgresql JSON interaction with this flag.

TypeEngine.should_evaluate_none - class-level flag

get_dbapi_type T0> ( T1> DBAPI T2> ) T3> ¶ T4>

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

默认情况下,这会调用基础“impl”的TypeEngine.get_dbapi_type()

literal_processor T0> ( T1> 方言 T2> ) T3> ¶ T4>

为给定的Dialect提供文字处理功能。

此处的子类通常会覆盖TypeDecorator.process_literal_param(),而不是直接使用此方法。

默认情况下,如果实现了该方法,则此方法使用TypeDecorator.process_bind_param(),其中TypeDecorator.process_literal_param()不是。The rationale here is that TypeDecorator typically deals with Python conversions of data that are above the layer of database presentation. With the value converted by TypeDecorator.process_bind_param(), the underlying type will then handle whether it needs to be presented to the DBAPI as a bound parameter or to the database as an inline SQL value.

版本0.9.0中新增。

load_dialect_impl T0> ( T1> 方言 T2> ) T3> ¶ T4>

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

这是一个最终用户重写钩子,可以根据给定的方言用来提供不同的类型。它由type_engine()TypeDecorator实现使用,以帮助确定哪个类型应该最终返回给定的TypeDecorator

默认返回self.impl

process_bind_param(value, dialect)

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

子类重写此方法以返回应传递给底层TypeEngine对象的值,并从那里返回到DBAPI execute()方法。

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

这个操作的设计应该考虑到逆向操作,这个操作就是这个类的process_result_value方法。

参数:
  • value – Data to operate upon, of any type expected by this method in the subclass. 可以是None
  • dialect – the Dialect in use.
process_literal_param(value, dialect)

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

编译器在不使用绑定的情况下呈现字面值,通常在DDL中,例如在列的“服务器默认值”或CHECK约束内的表达式中使用此方法。

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

版本0.9.0中新增。

process_result_value(value, dialect)

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

子类应该实现这个方法来操作从数据库中获取的数据。

给定一个已由底层TypeEngine对象处理的值,最初来自DBAPI游标方法fetchone()

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

参数:
  • value – Data to operate upon, of any type expected by this method in the subclass. 可以是None
  • dialect – the Dialect in use.

这个操作应该被设计成可以通过这个类的“process_bind_param”方法来反转。

python_type T0> ¶ T1>
继承自 python_type 属性 TypeEngine

如果已知,则返回预期由此类型的实例返回的Python类型对象。

基本上,对于强制执行返回类型的类型,或者在所有常见的DBAPI(例如int)中都可以这样做的类型,将返回该类型。

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

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

result_processor(dialect, coltype)

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

这是满足TypeEngine合同结果值转换的方法。TypeDecorator will wrap a user-defined implementation of process_result_value() here.

用户定义的代码可以直接覆盖这个方法,尽管最好使用process_result_value(),以保持self.impl提供的处理。

参数:
  • dialect – Dialect instance in use.
  • coltype - SQLAlchemy数据类型

此方法与此类的bind_processor()方法相反。

type_engine T0> ( T1> 方言 T2> ) T3> ¶ T4>

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

在大多数情况下,这返回由self.impl表示的TypeEngine类型的方言适应形式。使用dialect_impl(),但也遍历包装的TypeDecorator实例。可以通过覆盖load_dialect_impl()来定制行为。

with_variant typedialect_name
inherited from the with_variant() method of TypeEngine

产生一个新的类型对象,将其应用于给定名称的方言时使用给定的类型。

例如。:

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

s = String()

s = s.with_variant(mysql.VARCHAR(collation='foo'), 'mysql')

The construction of TypeEngine.with_variant() is always from the “fallback” type to that which is dialect specific. 返回的类型是Variant的一个实例,它本身提供了一个可重复调用的Variant.with_variant()

参数:
  • type_ – a TypeEngine that will be selected as a variant from the originating type, when a dialect of the given name is in use.
  • dialect_name – base name of the dialect which uses this type. (即'postgresql''mysql'等)

New in version 0.7.2.

TypeDecorator Recipes

接下来几个关键的TypeDecorator食谱。

将编码字符串强制为Unicode

A common source of confusion regarding the Unicode type is that it is intended to deal only with Python unicode objects on the Python side, meaning values passed to it as bind parameters must be of the form u'some string' if using Python 2 and not 3. 它所执行的编码/解码功能仅仅是为了适应所使用的DBAPI,并且主要是私有的实现细节。

The use case of a type that can safely receive Python bytestrings, that is strings that contain non-ASCII characters and are not u'' objects in Python 2, can be achieved using a TypeDecorator which coerces as needed:

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的连接器)会在小数位数过多的情况下传递小数位数。这里有一个食谱,他们倒过来:

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

后端不可知的GUID类型

接收并返回Python uuid()对象。在其他后端使用Postgresql,CHAR(32)时,使用PG UUID类型,并以字符串十六进制格式存储它们。如果需要,可以修改以在CHAR(16)中存储二进制文件:

from sqlalchemy.types import TypeDecorator, CHAR
from sqlalchemy.dialects.postgresql import UUID
import uuid

class GUID(TypeDecorator):
    """Platform-independent GUID type.

    Uses Postgresql's UUID type, otherwise uses
    CHAR(32), storing as stringified hex values.

    """
    impl = CHAR

    def load_dialect_impl(self, dialect):
        if dialect.name == 'postgresql':
            return dialect.type_descriptor(UUID())
        else:
            return dialect.type_descriptor(CHAR(32))

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        elif dialect.name == 'postgresql':
            return str(value)
        else:
            if not isinstance(value, uuid.UUID):
                return "%.32x" % uuid.UUID(value).int
            else:
                # hexstring
                return "%.32x" % value.int

    def process_result_value(self, value, dialect):
        if value is None:
            return value
        else:
            return uuid.UUID(value)

统帅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

    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将不会检测到这种类型的“可变性” - 这意味着,就地更改值不会被检测到,也不会被刷新。如果没有进一步的步骤,您将需要用每个父对象上的新值替换现有值以检测更改。请注意,这没有什么错,因为许多应用程序可能不需要一旦创建值就会发生变化。对于那些有此要求的用户,最好使用sqlalchemy.ext.mutable扩展名来应用可变性 - 请参阅Mutation Tracking中的示例。

替换现有类型的绑定/结果处理

在绑定/结果级别上,类型行为的大部分扩展是通过使用TypeDecorator来实现的。对于需要替换SQLAlchemy在DBAPI级别应用的特定处理的罕见场景,可以直接对SQLAlchemy类型进行子类化,并且可以使用bind_processor()result_processor()这样做需要重写adapt()方法。此方法是SQLAlchemy在执行语句期间生成特定于DBAPI的类型行为的机制。覆盖它可以使用自定义类型的副本来代替DBAPI特定的类型。下面我们将types.TIME类型进行子类化以具有自定义结果处理行为。process()函数将直接从DBAPI游标接收value

class MySpecialTime(TIME):
    def __init__(self, special_argument):
        super(MySpecialTime, self).__init__()
        self.special_argument = special_argument

    def result_processor(self, dialect, coltype):
        import datetime
        time = datetime.time
        def process(value):
            if value is not None:
                microseconds = value.microseconds
                seconds = value.seconds
                minutes = seconds / 60
                return time(
                          minutes / 60,
                          minutes % 60,
                          seconds - minutes * 60,
                          microseconds)
            else:
                return None
        return process

    def adapt(self, impltype):
        return MySpecialTime(self.special_argument)

应用SQL级绑定/结果处理

正如在Augmenting Existing TypesReplacing the Bind/Result Processing of Existing Types部分所见,SQLAlchemy允许在将参数发送到语句时调用Python函数以及从数据库加载结果行的时间,以及在将数据发送到数据库或从数据库发送时对其应用转换。也可以定义SQL级别的转换。这里的基本原理是,只有关系数据库包含一系列必要的函数,才能在应用程序和持久性格式之间强制传入和传出数据。例子包括使用数据库定义的加密/解密函数,以及处理地理数据的存储过程。对Postgresql的Postgis扩展包括一系列SQL函数,这些函数是将数据强制转换为特定格式所必需的。

Any TypeEngine, UserDefinedType or TypeDecorator subclass can include implementations of TypeEngine.bind_expression() and/or TypeEngine.column_expression(), which when defined to return a non-None value should return a ColumnElement expression to be injected into the SQL statement, either surrounding bound parameters or a column expression. 例如,要构建一个将所有传入数据应用于Postgis函数ST_GeomFromTextGeometry类型,并将所有传入数据应用于函数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)

We can apply the Geometry type into Table metadata and use it in a select() construct:

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 is applied to the columns clause so that the return value is run through the function before passing into a result set, and ST_GeomFromText is run on the bound parameter so that the passed-in value is converted:

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

The TypeEngine.column_expression() method interacts with the mechanics of the compiler such that the SQL expression does not interfere with the labeling of the wrapped expression. 例如,如果我们对我们表达式的label()呈现一个select(),则字符串标签将移动到包装表达式的外部:

print(select([geometry.c.geom_data.label('my_data')]))

输出:

SELECT ST_AsText(geometry.geom_data) AS my_data
FROM geometry

For an example of subclassing a built in type directly, we subclass postgresql.BYTEA to provide a PGPString, which will make use of the Postgresql pgcrypto extension to encrpyt/decrypt values transparently:

from sqlalchemy import create_engine, String, select, func, \
        MetaData, Table, Column, type_coerce

from sqlalchemy.dialects.postgresql import BYTEA

class PGPString(BYTEA):
    def __init__(self, passphrase, length=None):
        super(PGPString, self).__init__(length)
        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 = MetaData()
message = Table('message', metadata,
                Column('username', String(50)),
                Column('message',
                    PGPString("this is my passphrase", length=1000)),
            )

engine = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
with engine.begin() as conn:
    metadata.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")
        ))

INSERT和SELECT语句应用pgp_sym_encryptpgp_sym_decrypt函数:

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'}

版本0.8中的新增功能添加了TypeEngine.bind_expression()TypeEngine.column_expression()方法。

也可以看看:

PostGIS Integration

重新定义和创建新的操作符

SQLAlchemy Core定义了一组可用于所有列表达式的表达式运算符。Some of these operations have the effect of overloading Python’s built in operators; examples of such operators include ColumnOperators.__eq__() (table.c.somecolumn == 'foo'), ColumnOperators.__invert__() (~table.c.flag), and ColumnOperators.__add__() (table.c.x + table.c.y). Other operators are exposed as explicit methods on column expressions, such as ColumnOperators.in_() (table.c.value.in_(['x', 'y'])) and ColumnOperators.like() (table.c.value.like('%ed%')).

在所有情况下,Core表达式结构都会查询表达式的类型,以确定现有操作符的行为,以及查找不属于内置集合的其他操作符。The TypeEngine base class defines a root “comparison” implementation TypeEngine.Comparator, and many specific types provide their own sub-implementations of this class. 用户定义的TypeEngine.Comparator实现可以直接构建到特定类型的简单子类中,以覆盖或定义新的操作。下面,我们创建一个覆盖ColumnOperators.__add__()运算符的Integer子类。

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属性建立为引用一个新的类,继承TypeEngine.Comparator类与Integer类型相关联。

用法:

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

通过将TypeEngine.Comparator自身实例化为expr属性,通过拥有的SQL表达式查阅ColumnOperators.__add__()的实现。表达式系统的机制是这样的:操作继续递归直到一个表达式对象产生一个新的SQL表达式结构。Above, we could just as well have said self.expr.op("goofy")(other) instead of self.op("goofy")(other).

New methods added to a TypeEngine.Comparator are exposed on an owning SQL expression using a __getattr__ scheme, which exposes methods added to TypeEngine.Comparator onto the owning 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)

一元操作也是可能的。例如,要添加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 !

也可以看看:

TypeEngine.comparator_factory

0.8版新增:增强了表达式系统,支持在每个类型级别上定制运算符。

创建新类型

The UserDefinedType class is provided as a simple base class for defining entirely new database types. 用它来表示SQLAlchemy不知道的本地数据库类型。如果只需要Python翻译行为,请改用TypeDecorator

class sqlalchemy.types。 UserDefinedType

基础:sqlalchemy.types.TypeEngine

基于用户定义的类型。

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

import sqlalchemy.types as types

class MyType(types.UserDefinedType):
    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', meta,
    Column('id', Integer, primary_key=True),
    Column('data', MyType(16))
    )

The get_col_spec() method will in most cases receive a keyword argument type_expression which refers to the owning expression of the type as being compiled, such as a Column or cast() construct. 只有当方法接受参数签名中的关键字参数(例如**kw)时才会发送此关键字;内省是用来检查这个,以支持这个功能的遗留形式。

New in version 1.0.0: the owning expression is passed to the get_col_spec() method via the keyword argument type_expression, if it receives **kw in its signature.

coerce_compared_value opvalue

在表达式中建议一个“被强制的”Python值的类型。

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

在版本0.8中更改: UserDefinedType.coerce_compared_value()现在默认返回self,而不是落在TypeEngine.coerce_compared_value()