Commits

Mike Bayer committed 032888c

- added 'comparator' keyword argument to PickleType. By default, "mutable"
PickleType does a "deep compare" of objects using their dumps() representation.
But this doesn't work for dictionaries. Pickled objects which provide an
adequate __eq__() implementation can be set up with "PickleType(comparator=operator.eq)"
[ticket:560]

Comments (0)

Files changed (3)

   insert/update statement compilation process in terms of the column names
   present but not the values for those columns.  Produces more consistent
   execute/executemany behavior, simplifies things a bit internally.
+ 
+- added 'comparator' keyword argument to PickleType.  By default, "mutable"
+  PickleType does a "deep compare" of objects using their dumps() representation.  
+  But this doesn't work for dictionaries.  Pickled objects which provide an 
+  adequate __eq__() implementation can be set up with "PickleType(comparator=operator.eq)"
+  [ticket:560]
   
 - Fixed reflection of the empty string for mysql enums.
 

lib/sqlalchemy/types.py

 class PickleType(MutableType, TypeDecorator):
     impl = Binary
 
-    def __init__(self, protocol=pickle.HIGHEST_PROTOCOL, pickler=None, mutable=True):
+    def __init__(self, protocol=pickle.HIGHEST_PROTOCOL, pickler=None, mutable=True, comparator=None):
         self.protocol = protocol
         self.pickler = pickler or pickle
         self.mutable = mutable
+        self.comparator = comparator
         super(PickleType, self).__init__()
 
     def bind_processor(self, dialect):
             return value
 
     def compare_values(self, x, y):
-        if self.mutable:
+        if self.comparator:
+            return self.comparator(x, y)
+        elif self.mutable:
             return self.pickler.dumps(x, self.protocol) == self.pickler.dumps(y, self.protocol)
         else:
             return x is y

test/orm/unitofwork.py

             ),
         ])
         
-        
     def test_nocomparison(self):
         """test that types marked as MutableType get changes detected on them when the type has no __eq__ method"""
         class Foo(object):pass
         def go():
             Session.commit()
         self.assert_sql_count(testbase.db, go, 0)
+
+class MutableTypesTest2(ORMTest):
+    def define_tables(self, metadata):
+        global table
+        import operator
+        table = Table('mutabletest', metadata,
+            Column('id', Integer, Sequence('mutableidseq', optional=True), primary_key=True),
+            Column('data', PickleType(comparator=operator.eq)),
+            )
+    
+    def test_dicts(self):
+        """dictionaries dont pickle the same way twice, sigh."""
+
+        class Foo(object):pass
+        mapper(Foo, table)
+        f1 = Foo()
+        f1.data = [{'personne': {'nom': u'Smith', 'pers_id': 1, 'prenom': u'john', 'civilite': u'Mr', \
+                    'int_3': False, 'int_2': False, 'int_1': u'23', 'VenSoir': True, 'str_1': u'Test', \
+                    'SamMidi': False, 'str_2': u'chien', 'DimMidi': False, 'SamSoir': True, 'SamAcc': False}}]
+
+        Session.commit()
+        def go():
+            Session.commit()
+        self.assert_sql_count(testbase.db, go, 0)
+
+        f1.data = [{'personne': {'nom': u'Smith', 'pers_id': 1, 'prenom': u'john', 'civilite': u'Mr', \
+                    'int_3': False, 'int_2': False, 'int_1': u'23', 'VenSoir': True, 'str_1': u'Test', \
+                    'SamMidi': False, 'str_2': u'chien', 'DimMidi': False, 'SamSoir': True, 'SamAcc': False}}]
+
+        def go():
+            Session.commit()
+        self.assert_sql_count(testbase.db, go, 0)
+
+        f1.data[0]['personne']['VenSoir']= False
+        def go():
+            Session.commit()
+        self.assert_sql_count(testbase.db, go, 1)
         
+        Session.clear()
+        f = Session.query(Foo).get(f1.id)
+        assert f.data == [{'personne': {'nom': u'Smith', 'pers_id': 1, 'prenom': u'john', 'civilite': u'Mr', \
+                    'int_3': False, 'int_2': False, 'int_1': u'23', 'VenSoir': False, 'str_1': u'Test', \
+                    'SamMidi': False, 'str_2': u'chien', 'DimMidi': False, 'SamSoir': True, 'SamAcc': False}}]
         
 class PKTest(ORMTest):
     def define_tables(self, metadata):