]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added as_uuid=True flag to the UUID type, will receive
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 18 Nov 2010 23:44:35 +0000 (18:44 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 18 Nov 2010 23:44:35 +0000 (18:44 -0500)
and return values as Python UUID() objects rather than
strings.  Currently, the UUID type is only known to
work with psycopg2.  [ticket:1956]

CHANGES
lib/sqlalchemy/dialects/postgresql/base.py
test/dialect/test_postgresql.py

diff --git a/CHANGES b/CHANGES
index 4f65434559f8269e359c31832f8bb0be80de5b85..86fac65a2a614b0cd6602e31085670f686655ff2 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -57,7 +57,12 @@ CHANGES
   - Ensured every numeric, float, int code, scalar + array,
     are recognized by psycopg2 and pg8000's "numeric" 
     base type. [ticket:1955]
-    
+
+  - Added as_uuid=True flag to the UUID type, will receive
+    and return values as Python UUID() objects rather than
+    strings.  Currently, the UUID type is only known to 
+    work with psycopg2.  [ticket:1956]
+        
 - mysql
   - Fixed error handling for Jython + zxjdbc, such that 
     has_table() property works again.  Regression from
index 6eec651b1b0f234b98ea910c20445ec46e834447..7b1a97c325ef87380368d50d46cddaa07a2e703b 100644 (file)
@@ -94,6 +94,11 @@ from sqlalchemy.sql import compiler, expression, util as sql_util
 from sqlalchemy.sql import operators as sql_operators
 from sqlalchemy import types as sqltypes
 
+try:
+    from uuid import UUID as _python_UUID
+except ImportError:
+    _python_UUID = None
+
 from sqlalchemy.types import INTEGER, BIGINT, SMALLINT, VARCHAR, \
         CHAR, TEXT, FLOAT, NUMERIC, \
         DATE, BOOLEAN
@@ -134,6 +139,12 @@ class TIME(sqltypes.TIME):
         self.precision = precision
     
 class INTERVAL(sqltypes.TypeEngine):
+    """Postgresql INTERVAL type.
+    
+    The INTERVAL type may not be supported on all DBAPIs.
+    It is known to work on psycopg2 and not pg8000 or zxjdbc.
+    
+    """
     __visit_name__ = 'INTERVAL'
     def __init__(self, precision=None):
         self.precision = precision
@@ -156,17 +167,67 @@ class BIT(sqltypes.TypeEngine):
 PGBit = BIT
 
 class UUID(sqltypes.TypeEngine):
+    """Postgresql UUID type.
+    
+    Represents the UUID column type, interpreting
+    data either as natively returned by the DBAPI
+    or as Python uuid objects.
+
+    The UUID type may not be supported on all DBAPIs.
+    It is known to work on psycopg2 and not pg8000.
+    
+    """
     __visit_name__ = 'UUID'
+    
+    def __init__(self, as_uuid=False):
+        """Construct a UUID type.
+        
+        
+        :param as_uuid=False: if True, values will be interpreted
+         as Python uuid objects, converting to/from string via the
+         DBAPI.
+         
+         """
+        if as_uuid and _python_UUID is None:
+            raise NotImplementedError(
+                    "This version of Python does not support the native UUID type."
+                )
+        self.as_uuid = as_uuid
+    
+    def bind_processor(self, dialect):
+        if self.as_uuid:
+            def process(value):
+                if value is not None:
+                    value = str(value)
+                return value
+            return process
+        else:
+            return None
+            
+    def result_processor(self, dialect, coltype):
+        if self.as_uuid:
+            def process(value):
+                if value is not None:
+                    value = _python_UUID(value)
+                return value
+            return process
+        else:
+            return None
+            
 PGUuid = UUID
 
 class ARRAY(sqltypes.MutableType, sqltypes.Concatenable, sqltypes.TypeEngine):
     """Postgresql ARRAY type.
     
     Represents values as Python lists.
+
+    The ARRAY type may not be supported on all DBAPIs.
+    It is known to work on psycopg2 and not pg8000.
     
     **Note:** be sure to read the notes for 
-    :class:`~sqlalchemy.types.MutableType` regarding ORM 
-    performance implications.
+    :class:`.MutableType` regarding ORM 
+    performance implications.   The :class:`.ARRAY` type's 
+    mutability can be disabled using the "mutable" flag.
     
     """
     __visit_name__ = 'ARRAY'
index 54127cd9868df76fc537a1a9b7ebfc6b7de75af3..f3eb91ef264090f029b1e440586747bd7607fe88 100644 (file)
@@ -1884,6 +1884,64 @@ class SpecialTypesTest(TestBase, ComparesTables):
         assert t.c.plain_interval.type.precision is None
         assert t.c.precision_interval.type.precision == 3
 
+class UUIDTest(TestBase):
+    """Test the bind/return values of the UUID type."""
+    
+    __only_on__ = 'postgresql'
+    
+    @testing.fails_on('postgresql+pg8000', 'No support for UUID type')
+    def test_uuid_string(self):
+        import uuid
+        self._test_round_trip(
+            Table('utable', MetaData(), 
+                Column('data', postgresql.UUID())
+            ),
+            str(uuid.uuid4()),
+            str(uuid.uuid4())
+        )
+        
+    @testing.fails_on('postgresql+pg8000', 'No support for UUID type')
+    def test_uuid_uuid(self):
+        import uuid
+        self._test_round_trip(
+            Table('utable', MetaData(), 
+                Column('data', postgresql.UUID(as_uuid=True))
+            ),
+            uuid.uuid4(),
+            uuid.uuid4()
+        )
+    
+    def test_no_uuid_available(self):
+        from sqlalchemy.dialects.postgresql import base
+        uuid_type = base._python_UUID
+        base._python_UUID = None
+        try:
+            assert_raises(
+                NotImplementedError,
+                postgresql.UUID, as_uuid=True
+            )
+        finally:
+            base._python_UUID = uuid_type
+        
+    def setup(self):
+        self.conn = testing.db.connect()
+        trans = self.conn.begin()
+        
+    def teardown(self):
+        self.conn.close()
+        
+    def _test_round_trip(self, utable, value1, value2):
+        utable.create(self.conn)
+        self.conn.execute(utable.insert(), {'data':value1})
+        self.conn.execute(utable.insert(), {'data':value2})
+        r = self.conn.execute(
+                select([utable.c.data]).
+                    where(utable.c.data != value1)
+                )
+        eq_(r.fetchone()[0], value2)
+        eq_(r.fetchone(), None)
+        
+        
 class MatchTest(TestBase, AssertsCompiledSQL):
 
     __only_on__ = 'postgresql'