]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- The :class:`.TypeDecorator` type extender will now work in conjunction
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 27 Aug 2015 22:04:25 +0000 (18:04 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 27 Aug 2015 22:04:25 +0000 (18:04 -0400)
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

doc/build/changelog/changelog_11.rst
doc/build/changelog/migration_11.rst
lib/sqlalchemy/sql/type_api.py
test/dialect/postgresql/test_types.py
test/sql/test_types.py

index 7d076a35c8c3b5148ba67947a3a7d53692308aa7..4fff4ab64983bb341c393d086a6ecedd4690cabe 100644 (file)
 .. changelog::
     :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
index 3a0666dcc0b2c093eb387e4ab86f1f56e8447962..412f42d27a0e945b9178e73308c4c8f78562000c 100644 (file)
@@ -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
 ============================
 
index b9826e585c6c4111dbdbca7fa0ab59c7c37fbcad..f5ab1a8d32573b1000530fcf5f085cb4899979e8 100644 (file)
@@ -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`.
index 8eab9d4b97c5e860c2339b6de2462916572c12c5..7aad23255b99787ee34aeb777237a8594c49a016 100644 (file)
@@ -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'
index e32126a182d9121bef85c2c5a08e079bdf389a53..28848239279f98fd6dd7da9121414ea04f521c65 100644 (file)
@@ -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