Commits

Mike Bayer committed 9e8ef86

- [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]

Comments (0)

Files changed (3)

     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"/

lib/sqlalchemy/orm/properties.py

 
     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:
             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

test/orm/test_merge.py

 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):
     """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
         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)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.