Warn on merge of already-pending object

A warning is now emitted for the case where a transient object is being
merged into the session with :meth:`.Session.merge` when that object is
already transient in the :class:`.Session`.   This warns for the case where
the object would normally be double-inserted.

Fixes: #4647
Change-Id: Ie5223a59a2856664bf283017e962caf8c4230536
(cherry picked from commit 23a1c60982)
This commit is contained in:
Mike Bayer
2019-04-28 12:40:31 -04:00
parent 3c7bc0f9d7
commit 192d3ecfdc
3 changed files with 47 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
.. change::
:tags: bug, orm
:tickets: 4647
A warning is now emitted for the case where a transient object is being
merged into the session with :meth:`.Session.merge` when that object is
already transient in the :class:`.Session`. This warns for the case where
the object would normally be double-inserted.
+7
View File
@@ -2110,6 +2110,13 @@ class Session(_SessionClassMethods):
key = state.key
if key is None:
if state in self._new:
util.warn(
"Instance %s is already pending in this Session yet is "
"being merged again; this is probably not what you want "
"to do" % state_str(state)
)
if not load:
raise sa_exc.InvalidRequestError(
"merge() with load=False option does not support "
+31
View File
@@ -26,6 +26,7 @@ from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.orm.interfaces import MapperOption
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import eq_
from sqlalchemy.testing import expect_warnings
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import in_
from sqlalchemy.testing import not_in_
@@ -84,6 +85,36 @@ class MergeTest(_fixtures.FixtureTest):
self.assert_sql_count(testing.db, go, 0)
def test_warn_transient_already_pending_nopk(self):
User, users = self.classes.User, self.tables.users
mapper(User, users)
sess = create_session()
u = User(name="fred")
sess.add(u)
with expect_warnings(
"Instance <User.*> is already pending in this Session yet is "
"being merged again; this is probably not what you want to do"
):
u2 = sess.merge(u)
def test_warn_transient_already_pending_pk(self):
User, users = self.classes.User, self.tables.users
mapper(User, users)
sess = create_session()
u = User(id=1, name="fred")
sess.add(u)
with expect_warnings(
"Instance <User.*> is already pending in this Session yet is "
"being merged again; this is probably not what you want to do"
):
u2 = sess.merge(u)
def test_transient_to_pending_collection(self):
User, Address, addresses, users = (
self.classes.User,