存在多种方法来重新定义现有类型的行为并提供新的类型。
经常需要强制更改类型的“字符串”版本,即在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
,它将针对除SQLite以外的所有后端生成字符串BINARY
,在这种情况下,它将生成BLOB
有关其他示例,请参阅Changing Compilation of Types一节(Custom SQL Constructs and Compilation Extension的小节)。
TypeDecorator
允许创建自定义类型,将绑定参数和结果处理行为添加到现有的类型对象。当需要对数据库进行额外的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部分。
sqlalchemy.types.
TypeDecorator
(*args, **kwargs)¶基础:sqlalchemy.sql.expression.SchemaEventTarget
,sqlalchemy.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
默认情况下会“强制”非类型侧是相同的键入本身。如下所示,我们定义一个“纪元”类型,它将日期值存储为整数:
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.JSON
和postgresql.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
不是可调用的(不寻常的情况),它将被分配给同样的实例属性'as-is',忽略传递给构造函数的那些参数。
子类可以覆盖它来完全自定义self.impl
的生成。
adapt
(cls, **kw)¶adapt()
method of TypeEngine
产生这种类型的“适应”形式,给予一个“impl”类来处理。
此方法在内部用于将泛型与特定于特定方言的“实现”类型相关联。
bind_expression T0> ( T1> bindvalue T2> ) T3> ¶ T4>
bind_expression()
method of TypeEngine
“给定一个绑定值(即一个BindParameter
实例),在它的位置返回一个SQL表达式。
这通常是一个包含语句中现有绑定参数的SQL函数。它用于特殊数据类型,这些数据类型需要文字被封装在某些特殊的数据库函数中,以便将应用程序级别的值强制转换为数据库特定的格式。它是TypeEngine.bind_processor()
方法的SQL模拟。
该方法在语句编译时进行评估,而不是语句构建时间。
请注意,此方法在实现时应始终返回完全相同的结构,而不使用任何条件逻辑,因为它可用于针对任意数量的绑定参数集的executemany()调用。
也可以看看:
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实例正在使用中。 |
---|
此方法与此类的result_processor()
方法相反。
coerce_compared_value
( op,值 ) 为表达式中的'强制'Python值建议类型。
默认情况下,返回self。如果使用此类型的对象位于表达式的左侧或右侧,而不是指定了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>,) ¶使用==
进行比较时指定应该在表达式级别转换为“IS IS NOT 与
!=
结合
对于大多数SQLAlchemy类型,这包括NoneType
以及bool
。
TypeDecorator
modifies this list to only include NoneType
, as typedecorator implementations that deal with boolean types are common.
Custom TypeDecorator
类可以覆盖此属性以返回空元组,在这种情况下,不会将值强制为常量。
TypeDecorator.coerce_to_is_types
以便更容易地控制__eq__()
__ne__()
操作。 column_expression T0> ( T1> colexpr T2> ) T3> ¶ T4>
column_expression()
method of TypeEngine
给定一个SELECT列表达式,返回一个包装SQL表达式。
这通常是一个SQL函数,它包装列表达式,并将其呈现在SELECT语句的columns子句中。它用于特殊数据类型,这些数据类型需要将列包装在某些特殊的数据库函数中,以便在将值返回给应用程序之前强制值。它是TypeEngine.result_processor()
方法的SQL模拟。
该方法在语句编译时进行评估,而不是语句构建时间。
也可以看看:
compare_against_backend
( dialect,conn_type ) ¶compare_against_backend()
method of TypeEngine
将此类型与给定的后端类型进行比较。
此函数目前尚未针对SQLAlchemy类型实现,对于所有内置类型,此函数将返回None
。但是,它可以通过用户定义的类型实现,可以通过模式比较工具(如Alembic autogenerate)使用它。
未来的SQLAlchemy版本也可能会对这种内置类型的方法产生影响。
如果此类型与给定类型相同,则该函数应返回True;该类型通常反映在数据库中,因此应该是数据库特定的。使用的方言也通过了。它也可以返回False来声明该类型不相同。
参数: |
---|
版本1.0.3中的新功能
比较值
( x,y ) ¶给定两个值,比较他们的平等。
默认情况下,这会调用基础“impl”的TypeEngine.compare_values()
,后者通常使用Python等号运算符==
。
ORM使用该函数将原始加载的值与截获的“已更改”值进行比较,以确定是否发生了净更改。
编译 T0> ( T1> 方言=无 T2> ) T3> ¶ T4>
compile()
method of TypeEngine
生成此TypeEngine
的字符串编译形式。
当不带参数调用时,使用“默认”方言产生字符串结果。
参数: | dialect ¶ - a Dialect 实例。 |
---|
复制 T0> ( T1> **千瓦 T2> ) T3> ¶ T4>
生成此TypeDecorator
实例的副本。
这是一个浅层副本,用于履行TypeEngine
合约的一部分。通常不需要重写,除非用户定义的TypeDecorator
具有应该被深度复制的本地状态。
dialect_impl T0> ( T1> 方言 T2> ) T3> ¶ T4>
返回该TypeEngine
的特定于方言的实现。
evaluates_none T0> ( T1> ) T2> ¶ T3>
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交互。
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方法。
参数: |
---|
process_literal_param
(value, dialect)¶接收要在语句中内联呈现的文字参数值。
编译器在不使用绑定的情况下呈现文字值时,使用此方法,通常位于DDL中,例如列的“服务器默认值”或CHECK约束内的表达式。
返回的字符串将被渲染到输出字符串中。
版本0.9.0中的新功能
process_result_value
(value, dialect)¶接收要转换的结果行列值。
子类应实现此方法以对从数据库获取的数据进行操作。
给定一个已由底层TypeEngine
对象处理的值,最初来自DBAPI游标方法fetchone()
该操作可以是执行自定义行为所需的任何操作,例如转换或序列化数据。这也可以用作验证逻辑的钩子。
参数: |
---|
这个操作应该被设计成可以通过这个类的“process_bind_param”方法来反转。
python_type T0> ¶ T1>
python_type
attribute of 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
提供的处理。
参数: |
---|
此方法与此类的bind_processor()
方法相反。
type_engine
(dialect)¶为TypeDecorator
返回一个特定于方言的TypeEngine
实例。
在大多数情况下,这返回由self.impl
表示的TypeEngine
类型的方言适应形式。使用dialect_impl()
,但也遍历包装的TypeDecorator
实例。行为可以通过覆盖load_dialect_impl()
来定制。
with_variant
( type_,dialect_name ) 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')
TypeEngine.with_variant()
的构造始终是从“fallback”类型到特定于方言的。返回的类型是Variant
的一个实例,它本身提供了一个可重复调用的Variant.with_variant()
。
参数: |
|
---|
New in version 0.7.2.
接下来几个关键的TypeDecorator
食谱。
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的数据库连接器)会在小数位数过多的情况下传递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
接收并返回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)
这种类型使用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)
如在Augmenting Existing Types和Replacing the Bind/Result Processing of Existing Types部分所见,SQLAlchemy允许在将参数发送到语句时调用Python函数,如以及从数据库加载结果行时,以及在将数据发送到数据库或从数据库发送时对其应用转换。也可以定义SQL级别的转换。这里的基本原理是,只有关系数据库包含一系列必要的功能,才能在应用程序和持久性格式之间强制传入和传出数据。例子包括使用数据库定义的加密/解密函数,以及处理地理数据的存储过程。Postgis对Postgresql的扩展包括一系列SQL函数,这些函数是将数据强制转换为特定格式所必需的。
任何TypeEngine
,UserDefinedType
或TypeDecorator
子类都可以包含TypeEngine.bind_expression()
和/或TypeEngine.column_expression()
,当定义为返回非None
值时,应返回要注入SQL语句的ColumnElement
表达式,参数或列表达式。例如,要构建将所有传入数据应用于Postgis函数ST_GeomFromText
的所有传出值和函数ST_AsText
的Geometry
类型,我们可以创建我们自己的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
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
对于直接对内置类型进行子类化的示例,我们继承postgresql.BYTEA
以提供一个PGPString
,它将利用Postgresql pgcrypto
透明地扩展到encrpyt /解密值:
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")
))
pgp_sym_encrypt
和pgp_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定义了一组可用于所有列表达式的表达式运算符。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 !
也可以看看:
0.8版新增功能:增强了表达式系统,支持按类型级别定制运算符。
The UserDefinedType
class is provided as a simple base class for defining entirely new database types. 用它来表示SQLAlchemy不知道的本地数据库类型。如果只需要Python翻译行为,请改用TypeDecorator
。
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
)时才会发送此关键字;内省是用来检查这个以支持这个功能的遗留形式。
版本1.0.0新增:拥有的表达式通过关键字参数type_expression
传递给get_col_spec()
方法,如果它接收到**kw
签名。
coerce_compared_value
( op,值 ) 为表达式中的'强制'Python值建议类型。
UserDefinedType
的默认行为与TypeDecorator
的默认行为相同。默认情况下它会返回self
,假设比较值应该强制为与此类型相同的类型。有关更多详细信息,请参阅TypeDecorator.coerce_compared_value()
。
在版本0.8中改变: UserDefinedType.coerce_compared_value()
现在默认返回self
,而不是落在TypeEngine.coerce_compared_value()