summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-08-27 18:04:25 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2015-08-27 18:04:25 -0400
commited535649d423020c816e66869016992df25e456e (patch)
treed5a2dbbcf635a520e1fa350715acbec0ec15e571
parentd459afa8dbf73b8d9d620d09dede97e3461b6b3f (diff)
downloadsqlalchemy-ed535649d423020c816e66869016992df25e456e.tar.gz
- The :class:`.TypeDecorator` type extender will now work in conjunction
with a :class:`.SchemaType` implementation, typically :class:`.Enum` or :class:`.Boolean` with regards to ensuring that the per-table events are propagated from the implementation type to the outer type. These events are used to ensure that the constraints or Postgresql types (e.g. ENUM) are correctly created (and possibly dropped) along with the parent table. fixes #2919
-rw-r--r--doc/build/changelog/changelog_11.rst17
-rw-r--r--doc/build/changelog/migration_11.rst29
-rw-r--r--lib/sqlalchemy/sql/type_api.py15
-rw-r--r--test/dialect/postgresql/test_types.py28
-rw-r--r--test/sql/test_types.py10
5 files changed, 90 insertions, 9 deletions
diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst
index 7d076a35c..4fff4ab64 100644
--- a/doc/build/changelog/changelog_11.rst
+++ b/doc/build/changelog/changelog_11.rst
@@ -22,6 +22,23 @@
:version: 1.1.0b1
.. change::
+ :tags: bug, sql
+ :tickets: 2919
+
+ The :class:`.TypeDecorator` type extender will now work in conjunction
+ with a :class:`.SchemaType` implementation, typically :class:`.Enum`
+ or :class:`.Boolean` with regards to ensuring that the per-table
+ events are propagated from the implementation type to the outer type.
+ These events are used
+ to ensure that the constraints or Postgresql types (e.g. ENUM)
+ are correctly created (and possibly dropped) along with the parent
+ table.
+
+ .. seealso::
+
+ :ref:`change_2919`
+
+ .. change::
:tags: feature, sql
:tickets: 1370
diff --git a/doc/build/changelog/migration_11.rst b/doc/build/changelog/migration_11.rst
index 3a0666dcc..412f42d27 100644
--- a/doc/build/changelog/migration_11.rst
+++ b/doc/build/changelog/migration_11.rst
@@ -318,6 +318,35 @@ and :class:`.cume_dist`.
:ticket:`3132` :ticket:`1370`
+.. _change_2919:
+
+TypeDecorator now works with Enum, Boolean, "schema" types automatically
+------------------------------------------------------------------------
+
+The :class:`.SchemaType` types include types such as :class:`.Enum`
+and :class:`.Boolean` which, in addition to corresponding to a database
+type, also generate either a CHECK constraint or in the case of Postgresql
+ENUM a new CREATE TYPE statement, will now work automatically with
+:class:`.TypeDecorator` recipes. Previously, a :class:`.TypeDecorator` for
+an :class:`.postgresql.ENUM` had to look like this::
+
+ # old way
+ class MyEnum(TypeDecorator, SchemaType):
+ impl = postgresql.ENUM('one', 'two', 'three', name='myenum')
+
+ def _set_table(self, table):
+ self.impl._set_table(table)
+
+The :class:`.TypeDecorator` now propagates those additional events so it
+can be done like any other type::
+
+ # new way
+ class MyEnum(TypeDecorator):
+ impl = postgresql.ENUM('one', 'two', 'three', name='myenum')
+
+
+:ticket:`2919`
+
Key Behavioral Changes - ORM
============================
diff --git a/lib/sqlalchemy/sql/type_api.py b/lib/sqlalchemy/sql/type_api.py
index b9826e585..f5ab1a8d3 100644
--- a/lib/sqlalchemy/sql/type_api.py
+++ b/lib/sqlalchemy/sql/type_api.py
@@ -13,6 +13,7 @@
from .. import exc, util
from . import operators
from .visitors import Visitable, VisitableType
+from .base import SchemaEventTarget
# these are back-assigned by sqltypes.
BOOLEANTYPE = None
@@ -592,7 +593,7 @@ class UserDefinedType(util.with_metaclass(VisitableCheckKWArg, TypeEngine)):
return self
-class TypeDecorator(TypeEngine):
+class TypeDecorator(SchemaEventTarget, TypeEngine):
"""Allows the creation of types which add additional functionality
to an existing type.
@@ -772,6 +773,18 @@ class TypeDecorator(TypeEngine):
"""
return self.impl._type_affinity
+ def _set_parent(self, column):
+ """Support SchemaEentTarget"""
+
+ if isinstance(self.impl, SchemaEventTarget):
+ self.impl._set_parent(column)
+
+ def _set_parent_with_dispatch(self, parent):
+ """Support SchemaEentTarget"""
+
+ if isinstance(self.impl, SchemaEventTarget):
+ self.impl._set_parent_with_dispatch(parent)
+
def type_engine(self, dialect):
"""Return a dialect-specific :class:`.TypeEngine` instance
for this :class:`.TypeDecorator`.
diff --git a/test/dialect/postgresql/test_types.py b/test/dialect/postgresql/test_types.py
index 8eab9d4b9..7aad23255 100644
--- a/test/dialect/postgresql/test_types.py
+++ b/test/dialect/postgresql/test_types.py
@@ -499,6 +499,34 @@ class EnumTest(fixtures.TestBase, AssertsExecutionResults):
finally:
metadata.drop_all()
+ @testing.provide_metadata
+ def test_custom_subclass(self):
+ class MyEnum(TypeDecorator):
+ impl = Enum('oneHI', 'twoHI', 'threeHI', name='myenum')
+
+ def process_bind_param(self, value, dialect):
+ if value is not None:
+ value += "HI"
+ return value
+
+ def process_result_value(self, value, dialect):
+ if value is not None:
+ value += "THERE"
+ return value
+
+ t1 = Table(
+ 'table1', self.metadata,
+ Column('data', MyEnum())
+ )
+ self.metadata.create_all(testing.db)
+
+ with testing.db.connect() as conn:
+ conn.execute(t1.insert(), {"data": "two"})
+ eq_(
+ conn.scalar(select([t1.c.data])),
+ "twoHITHERE"
+ )
+
class OIDTest(fixtures.TestBase):
__only_on__ = 'postgresql'
diff --git a/test/sql/test_types.py b/test/sql/test_types.py
index e32126a18..288482392 100644
--- a/test/sql/test_types.py
+++ b/test/sql/test_types.py
@@ -1178,16 +1178,13 @@ class EnumTest(AssertsCompiledSQL, fixtures.TestBase):
def __init__(self, name):
self.name = name
- class MyEnum(types.SchemaType, TypeDecorator):
+ class MyEnum(TypeDecorator):
def __init__(self, values):
self.impl = Enum(
*[v.name for v in values], name="myenum",
native_enum=False)
- def _set_table(self, table, column):
- self.impl._set_table(table, column)
-
# future method
def process_literal_param(self, value, dialect):
return value.name
@@ -2007,12 +2004,9 @@ class BooleanTest(
def __init__(self, value):
self.value = value
- class MyBool(types.SchemaType, TypeDecorator):
+ class MyBool(TypeDecorator):
impl = Boolean()
- def _set_table(self, table, column):
- self.impl._set_table(table, column)
-
# future method
def process_literal_param(self, value, dialect):
return value.value