Overriding Type Compilation
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The string produced by any type object, when rendered in a CREATE TABLE
-statement or other SQL function like CAST, can be changed. See the
-section :ref:`type_compilation_extension`, a subsection of
-:ref:`sqlalchemy.ext.compiler_toplevel`, for a short example.
+A frequent need is to force the "string" version of a type, that is
+the one rendered in a CREATE TABLE statement or other SQL function
+like CAST, to be changed. For example, an application may want
+to force the rendering of ``BINARY`` for all platforms
+except for one, in which is wants ``BLOB`` to be rendered. Usage
+of an existing generic type, in this case :class:`.LargeBinary`, is
+preferred for most use cases. But to control
+types more accurately, a compilation directive that is per-dialect
+can be associated with any type::
+
+ from sqlalchemy.ext.compiler import compiles
+ from sqlalchemy.types import BINARY
+
+ @compiles(BINARY, "sqlite")
+ def compile_binary_sqlite(type_, compiler, **kw):
+ return "BLOB"
+
+The above code allows the usage of :class:`.types.BINARY`, which
+will produce the string ``BINARY`` against all backends except SQLite,
+in which case it will produce ``BLOB``.
+
+See the section :ref:`type_compilation_extension`, a subsection of
+:ref:`sqlalchemy.ext.compiler_toplevel`, for additional examples.
Augmenting Existing Types
~~~~~~~~~~~~~~~~~~~~~~~~~
return dialect.type_descriptor(self)
def adapt(self, cls, **kw):
+ """Produce an "adapted" form of this type, given an "impl" class
+ to work with.
+
+ This method is used internally to associate generic
+ types with "implementation" types that are specific to a particular
+ dialect.
+ """
return util.constructor_copy(self, cls, **kw)
def _coerce_compared_value(self, op, value):
return self._type_affinity is other._type_affinity
def compile(self, dialect=None):
+ """Produce a string-compiled form of this :class:`.TypeEngine`.
+
+ When called with no arguments, uses a "default" dialect
+ to produce a string result.
+
+ :param dialect: a :class:`.Dialect` instance.
+
+ """
# arg, return value is inconsistent with
# ClauseElement.compile()....this is a mistake.
__visit_name__ = "type_decorator"
def __init__(self, *args, **kwargs):
+ """Construct a :class:`.TypeDecorator`.
+
+ Arguments sent here are passed to the constructor
+ of the class assigned to the ``impl`` class level attribute,
+ where the ``self.impl`` attribute is assigned an instance
+ of the implementation type. If ``impl`` at the class level
+ is already an instance, then it's assigned to ``self.impl``
+ as is.
+
+ Subclasses can override this to customize the generation
+ of ``self.impl``.
+
+ """
if not hasattr(self.__class__, 'impl'):
raise AssertionError("TypeDecorator implementations "
"require a class-level variable "
return self.impl._type_affinity
def type_engine(self, dialect):
- """Return a TypeEngine instance for this TypeDecorator.
+ """Return a dialect-specific :class:`.TypeEngine` instance for this :class:`.TypeDecorator`.
+
+ In most cases this returns a dialect-adapted form of
+ the :class:`.TypeEngine` type represented by ``self.impl``.
"""
adapted = dialect.type_descriptor(self)
return getattr(self.impl, key)
def process_bind_param(self, value, dialect):
+ """Receive a bound parameter value to be converted.
+
+ Subclasses override this method to return the
+ value that should be passed along to the underlying
+ :class:`.TypeEngine` object, and from there to the
+ DBAPI ``execute()`` method.
+
+ :param value: the value. Can be None.
+ :param dialect: the :class:`.Dialect` in use.
+
+ """
raise NotImplementedError()
def process_result_value(self, value, dialect):
+ """Receive a result-row column value to be converted.
+
+ Subclasses override this method to return the
+ value that should be passed back to the application,
+ given a value that is already processed by
+ the underlying :class:`.TypeEngine` object, originally
+ from the DBAPI cursor method ``fetchone()`` or similar.
+
+ :param value: the value. Can be None.
+ :param dialect: the :class:`.Dialect` in use.
+
+ """
raise NotImplementedError()
def bind_processor(self, dialect):
+ """Provide a bound value processing function for the given :class:`.Dialect`.
+
+ This is the method that fulfills the :class:`.TypeEngine`
+ contract for bound value conversion. :class:`.TypeDecorator`
+ will wrap a user-defined implementation of
+ :meth:`process_bind_param` here.
+
+ User-defined code can override this method directly,
+ though its likely best to use :meth:`process_bind_param` so that
+ the processing provided by ``self.impl`` is maintained.
+
+ """
if self.__class__.process_bind_param.func_code \
is not TypeDecorator.process_bind_param.func_code:
process_param = self.process_bind_param
return self.impl.bind_processor(dialect)
def result_processor(self, dialect, coltype):
+ """Provide a result value processing function for the given :class:`.Dialect`.
+
+ This is the method that fulfills the :class:`.TypeEngine`
+ contract for result value conversion. :class:`.TypeDecorator`
+ will wrap a user-defined implementation of
+ :meth:`process_result_value` here.
+
+ User-defined code can override this method directly,
+ though its likely best to use :meth:`process_result_value` so that
+ the processing provided by ``self.impl`` is maintained.
+
+ """
if self.__class__.process_result_value.func_code \
is not TypeDecorator.process_result_value.func_code:
process_value = self.process_result_value
return self.coerce_compared_value(op, value)
def copy(self):
+ """Produce a copy of this :class:`.TypeDecorator` instance.
+
+ This is a shallow copy and is provided to fulfill part of
+ the :class:`.TypeEngine` contract. It usually does not
+ need to be overridden unless the user-defined :class:`.TypeDecorator`
+ has local state that should be deep-copied.
+
+ """
instance = self.__class__.__new__(self.__class__)
instance.__dict__.update(self.__dict__)
return instance
def get_dbapi_type(self, dbapi):
+ """Return the DBAPI type object represented by this :class:`.TypeDecorator`.
+
+ By default this calls upon :meth:`.TypeEngine.get_dbapi_type` of the
+ underlying "impl".
+ """
return self.impl.get_dbapi_type(dbapi)
def copy_value(self, value):
+ """Given a value, produce a copy of it.
+
+ By default this calls upon :meth:`.TypeEngine.copy_value`
+ of the underlying "impl".
+
+ :meth:`.copy_value` will return the object
+ itself, assuming "mutability" is not enabled.
+ Only the :class:`.MutableType` mixin provides a copy
+ function that actually produces a new object.
+ The copying function is used by the ORM when
+ "mutable" types are used, to memoize the original
+ version of an object as loaded from the database,
+ which is then compared to the possibly mutated
+ version to check for changes.
+
+ Modern implementations should use the
+ ``sqlalchemy.ext.mutable`` extension described in
+ :ref:`mutable_toplevel` for intercepting in-place
+ changes to values.
+
+ """
return self.impl.copy_value(value)
def compare_values(self, x, y):
+ """Given two values, compare them for equality.
+
+ By default this calls upon :meth:`.TypeEngine.compare_values`
+ of the underlying "impl", which in turn usually
+ uses the Python equals operator ``==``.
+
+ This function is used by the ORM to compare
+ an original-loaded value with an intercepted
+ "changed" value, to determine if a net change
+ has occurred.
+
+ """
return self.impl.compare_values(x, y)
def is_mutable(self):