summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES4
-rw-r--r--lib/sqlalchemy/orm/session.py6
-rw-r--r--test/orm/test_naturalpks.py56
3 files changed, 64 insertions, 2 deletions
diff --git a/CHANGES b/CHANGES
index 432c9e2a3..fe6fad202 100644
--- a/CHANGES
+++ b/CHANGES
@@ -379,6 +379,10 @@ CHANGES
bidirectional reference to declare itself as "viewonly"
[ticket:1507]
+ - Fixed bug which prevented two entities from mutually
+ replacing each other's primary key values within a single
+ flush() for some orderings of operations. [ticket:1519]
+
- Fixed an obscure issue whereby a joined-table subclass
with a self-referential eager load on the base class
would populate the related object's "subclass" table with
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index fdf679a42..002ccd509 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -1017,8 +1017,10 @@ class Session(object):
if state.key is None:
state.key = instance_key
elif state.key != instance_key:
- # primary key switch
- self.identity_map.remove(state)
+ # primary key switch.
+ # use discard() in case another state has already replaced this
+ # one in the identity map (see test/orm/test_naturalpks.py ReversePKsTest)
+ self.identity_map.discard(state)
state.key = instance_key
self.identity_map.replace(state)
diff --git a/test/orm/test_naturalpks.py b/test/orm/test_naturalpks.py
index e99bfb794..82a2f9e56 100644
--- a/test/orm/test_naturalpks.py
+++ b/test/orm/test_naturalpks.py
@@ -365,7 +365,63 @@ class NaturalPKTest(_base.MappedTest):
r = sess.query(Item).with_parent(u2).all()
eq_(Item(itemname='item2'), r[0])
+class ReversePKsTest(_base.MappedTest):
+ """reverse the primary keys of two entities and ensure bookkeeping succeeds."""
+
+
+ @classmethod
+ def define_tables(cls, metadata):
+ Table(
+ 'user', metadata,
+ Column('code', Integer, primary_key=True),
+ Column('status', Integer, primary_key=True),
+ Column('username', String, nullable=False),
+ )
+
+ @classmethod
+ def setup_classes(cls):
+ class User(_base.ComparableEntity):
+ def __init__(self, code, status, username):
+ self.code = code
+ self.status = status
+ self.username = username
+
+ @testing.resolve_artifact_names
+ def test_reverse(self):
+ PUBLISHED, EDITABLE, ARCHIVED = 1, 2, 3
+
+ mapper(User, user)
+
+ session = sa.orm.sessionmaker()()
+
+ a_published = User(0, PUBLISHED, u'a')
+ session.add(a_published)
+ session.commit()
+
+ a_editable = User(0, EDITABLE, u'a')
+
+ session.add(a_editable)
+ session.commit()
+
+ # do the switch in both directions -
+ # one or the other should raise the error
+ # based on platform dictionary ordering
+ a_published.status = ARCHIVED
+ a_editable.status = PUBLISHED
+
+ session.commit()
+ assert session.query(User).get([0, PUBLISHED]) is a_editable
+ assert session.query(User).get([0, ARCHIVED]) is a_published
+
+ a_published.status = PUBLISHED
+ a_editable.status = EDITABLE
+
+ session.commit()
+
+ assert session.query(User).get([0, PUBLISHED]) is a_published
+ assert session.query(User).get([0, EDITABLE]) is a_editable
+
class SelfRefTest(_base.MappedTest):
__unsupported_on__ = 'mssql' # mssql doesn't allow ON UPDATE on self-referential keys