- attached sqla1.diff
INSERTs and UPDATEs break when polymorphic discriminator is part of the primary key
It appears that INSERTs and UPDATEs do not work under the following conditions: 1. Table with composite primary key. 2. A hierarchy of classes polymorphically mapped to the table (single inheritance) 3. The polymorphic discriminator column is part of the primary key.
Also, while the 'insert' problem can be worked around (see bellow), fixing the 'update' requires patching SQLAlchemy's code.
Here's a test case:
from sqlalchemy import *
from sqlalchemy.orm import *
engine = create_engine('sqlite://')
metadata = MetaData()
test_table = Table("test", metadata,
Column("id1", Integer, primary_key=True),
Column("id2", UnicodeText, primary_key=True),
Column("data", UnicodeText)
)
class TestBase(object):
pass
class TestDeriv(TestBase):
pass
mapper(TestBase, test_table,
polymorphic_on=test_table.c.id2,
polymorphic_identity=u"base")
mapper(TestDeriv,
inherits=TestBase,
polymorphic_on=test_table.c.id2,
polymorphic_identity=u"deriv")
metadata.create_all(bind=engine)
session = sessionmaker(bind=engine, autoflush=False)()
t1 = TestDeriv()
t1.id1 = 0
# id2 is implied by instance's type, but we still
# have to assign it explicitly in order the insert to work
t1.id2 = u"deriv"
session.add(t1)
session.commit()
session.clear()
del t1
t1 = session.query(TestDeriv).get((0, u"deriv"))
t1.data = u"foo bar"
# The following line raises ConcurrentModificationError
# Logged SQL shows that id2 is bound to None
session.commit()
The fix (see the attached file, works against rev. 5098) is next to trivial (it only modifies two lines), but I have no way to be sure that it won't break something else.
Comments (4)
-
Account Deleted -
repo owner I haven't had time to add new unit tests for this use case, but this approach allows all existing inheritance tests to pass...
Index: lib/sqlalchemy/orm/mapper.py =================================================================== --- lib/sqlalchemy/orm/mapper.py (revision 5092) +++ lib/sqlalchemy/orm/mapper.py (working copy) @@ -1114,10 +1114,6 @@ for col in mapper._cols_by_table[table](table): if col is mapper.version_id_col: params[col.key](col.key) = 1 - elif col in pks: - value = mapper._get_state_attr_by_column(state, col) - if value is not None: - params[col.key](col.key) = value elif mapper.polymorphic_on and mapper.polymorphic_on.shares_lineage(col): if self._should_log_debug: self.__log_debug("Using polymorphic identity '%s' for insert column '%s'" % (mapper.polymorphic_identity, col.key)) @@ -1126,6 +1122,10 @@ col.server_default is None) or value is not None): params[col.key](col.key) = value + elif col in pks: + value = mapper._get_state_attr_by_column(state, col) + if value is not None: + params[col.key](col.key) = value else: value = mapper._get_state_attr_by_column(state, col) if ((col.default is None and @@ -1145,8 +1145,8 @@ (added, unchanged, deleted) = attributes.get_history(state, prop.key, passive=True) if added: hasdata = True - elif mapper.polymorphic_on and mapper.polymorphic_on.shares_lineage(col): - pass +# elif mapper.polymorphic_on and mapper.polymorphic_on.shares_lineage(col): +# pass else: if post_update_cols is not None and col not in post_update_cols: if col in pks:
-
repo owner - changed status to duplicate
#1300is a dupe of this and has been fixed. -
repo owner - removed milestone
Removing milestone: 0.5.xx (automated comment)
- Log in to comment
Proposed fix.