]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- [bug] Fixed bug whereby polymorphic_on
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 31 Mar 2012 16:55:42 +0000 (12:55 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 31 Mar 2012 16:55:42 +0000 (12:55 -0400)
    column that's not otherwise mapped on the
    class would be incorrectly included
    in a merge() operation, raising an error.
    [ticket:2449]

CHANGES
lib/sqlalchemy/orm/properties.py
test/orm/test_merge.py

diff --git a/CHANGES b/CHANGES
index 57f30404bd8b1998d2d99f03957822c1888f2378..03173beed54441f1e4ccf6784cfd68874e310345 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,12 @@ CHANGES
     directives in statements.  Courtesy
     Diana Clarke [ticket:2443]
 
+  - [bug] Fixed bug whereby polymorphic_on
+    column that's not otherwise mapped on the 
+    class would be incorrectly included
+    in a merge() operation, raising an error.
+    [ticket:2449]
+
 - postgresql
   - [feature] Added new for_update/with_lockmode()
     options for Postgresql: for_update="read"/
index 59c4cb3dc1932ba46dec9e71f4c8ee8a95a5ec63..74ccf0157f3d37ad906bb379c27fe1ad94c31f98 100644 (file)
@@ -136,7 +136,9 @@ class ColumnProperty(StrategizedProperty):
 
     def merge(self, session, source_state, source_dict, dest_state, 
                                 dest_dict, load, _recursive):
-        if self.key in source_dict:
+        if not self.instrument:
+            return
+        elif self.key in source_dict:
             value = source_dict[self.key]
 
             if not load:
@@ -144,9 +146,8 @@ class ColumnProperty(StrategizedProperty):
             else:
                 impl = dest_state.get_impl(self.key)
                 impl.set(dest_state, dest_dict, value, None)
-        else:
-            if dest_state.has_identity and self.key not in dest_dict:
-                dest_state.expire_attributes(dest_dict, [self.key])
+        elif dest_state.has_identity and self.key not in dest_dict:
+            dest_state.expire_attributes(dest_dict, [self.key])
 
     class Comparator(PropComparator):
         @util.memoized_instancemethod
index f7d9a4d3139323d50342aaf575f98ef42510696e..5885a4bda771a1d070cdd3ea7ef1498ac38adf79 100644 (file)
@@ -12,7 +12,7 @@ from sqlalchemy.orm.interfaces import MapperOption
 from test.lib.testing import eq_, ne_
 from test.lib import fixtures
 from test.orm import _fixtures
-from sqlalchemy import event, and_
+from sqlalchemy import event, and_, case
 from test.lib.schema import Table, Column
 
 class MergeTest(_fixtures.FixtureTest):
@@ -1102,7 +1102,7 @@ class M2ONoUseGetLoadingTest(fixtures.MappedTest):
     """Merge a one-to-many.  The many-to-one on the other side is set up 
     so that use_get is False.   See if skipping the "m2o" merge
     vs. doing it saves on SQL calls.
-    
+
     """
 
     @classmethod
@@ -1342,3 +1342,62 @@ class LoadOnPendingTest(fixtures.MappedTest):
         be able to merge()"""
         self._setup_delete_orphan_o2o()
         self._merge_delete_orphan_o2o_with(self.classes.Bug(id=1))
+
+class PolymorphicOnTest(fixtures.MappedTest):
+    """Test merge() of polymorphic object when polymorphic_on 
+    isn't a Column"""
+
+    @classmethod
+    def define_tables(cls, metadata):
+        Table('employees', metadata,
+            Column('employee_id', Integer, primary_key=True, 
+                            test_needs_autoincrement=True),
+            Column('type', String(1), nullable=False),
+            Column('data', String(50)),
+        )
+
+    @classmethod
+    def setup_classes(cls):
+        class Employee(cls.Basic, fixtures.ComparableEntity):
+            pass
+        class Manager(Employee):
+            pass
+        class Engineer(Employee):
+            pass
+
+    def _setup_polymorphic_on_mappers(self):
+        employee_mapper = mapper(self.classes.Employee, 
+            self.tables.employees,
+            polymorphic_on=case(value=self.tables.employees.c.type, 
+                whens={
+                    'E': 'employee',
+                    'M': 'manager',
+                    'G': 'engineer',
+                    'R': 'engineer',
+                    }),
+            polymorphic_identity='employee')
+        mapper(self.classes.Manager, inherits=employee_mapper,
+            polymorphic_identity='manager')
+        mapper(self.classes.Engineer, inherits=employee_mapper,
+            polymorphic_identity='engineer')
+        self.sess = sessionmaker()()
+
+    def test_merge_polymorphic_on(self):
+        """merge() should succeed with a polymorphic object even when
+        polymorphic_on is not a Column
+        """
+        self._setup_polymorphic_on_mappers()
+
+        m = self.classes.Manager(employee_id=55, type='M', 
+                                data='original data')
+        self.sess.add(m)
+        self.sess.commit()
+        self.sess.expunge_all()
+
+        m = self.classes.Manager(employee_id=55, data='updated data')
+        merged = self.sess.merge(m)
+
+        # we've already passed ticket #2449 problem since 
+        # merge() returned, but for good measure:
+        assert m is not merged
+        eq_(m,merged)