]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Move pk on single-inh subclass check below conflict resolution check
authorTom Manderson <me@trm.io>
Tue, 30 Oct 2018 17:05:43 +0000 (13:05 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 30 Oct 2018 17:21:52 +0000 (13:21 -0400)
The column conflict resolution technique discussed at
:ref:`declarative_column_conflicts` is now functional for a :class:`.Column`
that is also a primary key column.  Previously, a check for primary key
columns declared on a single-inheritance subclass would occur before the
column copy were allowed to pass.

Fixes: #4352
Change-Id: Id4c025da53c28e58db6b549fe398f25f8a90d355
Pull-request: https://github.com/zzzeek/sqlalchemy/pull/483

doc/build/changelog/unreleased_12/4352.rst [new file with mode: 0644]
doc/build/orm/extensions/declarative/inheritance.rst
lib/sqlalchemy/ext/declarative/base.py
test/ext/declarative/test_inheritance.py

diff --git a/doc/build/changelog/unreleased_12/4352.rst b/doc/build/changelog/unreleased_12/4352.rst
new file mode 100644 (file)
index 0000000..7db01f6
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+   :tags: bug, orm, declarative
+   :tickets: 4352
+
+   The column conflict resolution technique discussed at
+   :ref:`declarative_column_conflicts` is now functional for a :class:`.Column`
+   that is also a primary key column.  Previously, a check for primary key
+   columns declared on a single-inheritance subclass would occur before the
+   column copy were allowed to pass.
+
index 25ce986b7d89eded03caa20085c1a70124c4b798..6ab3e10e710f4b97d48dbea37169404f04f8e341 100644 (file)
@@ -88,6 +88,8 @@ The attribute exclusion logic is provided by the
 behavior can be disabled by passing an explicit ``exclude_properties``
 collection (empty or otherwise) to the ``__mapper_args__``.
 
+.. _declarative_column_conflicts:
+
 Resolving Column Conflicts
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
index a6642364dc19bf0bb251f09565e7b49f6a4be1eb..e5bc670b3b20544e34d0a99ee4cb850f8fcd814f 100644 (file)
@@ -534,11 +534,6 @@ class _MapperConfig(object):
                     )
                 # add any columns declared here to the inherited table.
                 for c in declared_columns:
-                    if c.primary_key:
-                        raise exc.ArgumentError(
-                            "Can't place primary key columns on an inherited "
-                            "class with no table."
-                        )
                     if c.name in inherited_table.c:
                         if inherited_table.c[c.name] is c:
                             continue
@@ -547,6 +542,11 @@ class _MapperConfig(object):
                             "existing column '%s'" %
                             (c, cls, inherited_table.c[c.name])
                         )
+                    if c.primary_key:
+                        raise exc.ArgumentError(
+                            "Can't place primary key columns on an inherited "
+                            "class with no table."
+                        )
                     inherited_table.append_column(c)
                     if inherited_mapped_table is not None and \
                             inherited_mapped_table is not inherited_table:
index c310b725f4feaaaa850ee9860b395d9f2c822130..a453bc165c45aacde7e3235a89db59af5f4e2e82 100644 (file)
@@ -762,6 +762,75 @@ class DeclarativeInheritanceTest(DeclarativeTestBase):
         session.commit()
         eq_(session.query(Engineer).first().target, o1)
 
+    def test_columns_single_inheritance_conflict_resolution_pk(self):
+        """Test #2472 in terms of a primary key column.  This is
+        #4352.
+
+        """
+        class Person(Base):
+            __tablename__ = 'person'
+            id = Column(Integer, primary_key=True)
+
+            target_id = Column(Integer, primary_key=True)
+
+        class Engineer(Person):
+
+            """single table inheritance"""
+
+            @declared_attr
+            def target_id(cls):
+                return cls.__table__.c.get(
+                    'target_id',
+                    Column(Integer, primary_key=True))
+
+        class Manager(Person):
+
+            """single table inheritance"""
+
+            @declared_attr
+            def target_id(cls):
+                return cls.__table__.c.get(
+                    'target_id',
+                    Column(Integer, primary_key=True))
+
+        is_(
+            Engineer.target_id.property.columns[0],
+            Person.__table__.c.target_id
+        )
+        is_(
+            Manager.target_id.property.columns[0],
+            Person.__table__.c.target_id
+        )
+
+    def test_columns_single_inheritance_cascading_resolution_pk(self):
+        """An additional test for #4352 in terms of the requested use case.
+
+        """
+        class TestBase(Base):
+            __abstract__ = True
+
+            @declared_attr.cascading
+            def id(cls):
+                col_val = None
+                if TestBase not in cls.__bases__:
+                    col_val = cls.__table__.c.get('id')
+                if col_val is None:
+                    col_val = Column(Integer, primary_key=True)
+                return col_val
+
+        class Person(TestBase):
+            """single table base class"""
+            __tablename__ = 'person'
+
+        class Engineer(Person):
+            """ single table inheritance, no extra cols """
+
+        class Manager(Person):
+            """ single table inheritance, no extra cols """
+
+        is_(Engineer.id.property.columns[0], Person.__table__.c.id)
+        is_(Manager.id.property.columns[0], Person.__table__.c.id)
+
     def test_joined_from_single(self):
 
         class Company(Base, fixtures.ComparableEntity):