]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
warnings: cascade_backrefs
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 28 Oct 2021 21:10:15 +0000 (17:10 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 29 Oct 2021 00:04:43 +0000 (20:04 -0400)
this one is a little different in that the thing changing
is the detection of a behavior, not an explicit API.

Change-Id: Id142943a2b901b39fe9053d0120c1e820dc1a6d0

15 files changed:
doc/build/orm/tutorial.rst
lib/sqlalchemy/orm/strategies.py
lib/sqlalchemy/testing/warnings.py
test/ext/test_associationproxy.py
test/orm/test_backref_mutations.py
test/orm/test_cascade.py
test/orm/test_deprecations.py
test/orm/test_expire.py
test/orm/test_lazy_relations.py
test/orm/test_load_on_fks.py
test/orm/test_mapper.py
test/orm/test_merge.py
test/orm/test_naturalpks.py
test/orm/test_session.py
test/orm/test_versioning.py

index 1f99a503a48e1042d2ff8e9cbc6e27ce8c924a4b..fab80da4a8486b2845d3844371f495a196abae20 100644 (file)
@@ -347,6 +347,10 @@ connect it to the :class:`~sqlalchemy.orm.session.Session` using
 
     >>> Session.configure(bind=engine)  # once engine is available
 
+..  Setup code, not for display - ensure no cascade_backrefs warnings occur
+
+    >>> Session.configure(future=True)
+
 .. sidebar:: Session Lifecycle Patterns
 
     The question of when to make a :class:`.Session` depends a lot on what
index 4f361be2c9dbe2bb139ffa94a450afae48ab9f79..2a283caad6e12fcdb1bca47c9fbafb3bc1ffb571 100644 (file)
@@ -797,7 +797,6 @@ class LazyLoader(AbstractRelationshipLoader, util.MemoizedSlots):
         )
 
     def _load_for_state(self, state, passive, loadopt=None, extra_criteria=()):
-
         if not state.key and (
             (
                 not self.parent_property.load_on_pending
index dc1286295c2219500bdf433eb3a9a0a0f7743841..45d831fe1c228a0d28ead601486c04d0e8a9b03c 100644 (file)
@@ -75,8 +75,6 @@ def setup_filters():
         # ORM Session
         #
         r"The Session.autocommit parameter is deprecated ",
-        r".*object is being merged into a Session along the backref "
-        "cascade path",
         r"The merge_result\(\) method is superseded by the "
         r"merge_frozen_result\(\)",
         r"The Session.begin.subtransactions flag is deprecated",
index 258ecb90c4f6988af7c0d81ef70176de8a3ef76b..3e6652d1e4025381d2e45d6a07110dd18ad98cf4 100644 (file)
@@ -146,7 +146,9 @@ class AutoFlushTest(fixtures.MappedTest):
             collection_class, is_dict=is_dict
         )
 
-        session = Session(testing.db, autoflush=True, expire_on_commit=True)
+        session = Session(
+            testing.db, autoflush=True, expire_on_commit=True, future=True
+        )
 
         p1 = Parent()
         c1 = Child("c1")
index fd5d908cf50ba5e8986bc6d20e177cc997f86a2a..0f10cff2481e5fd843644652205d624cf585e4cc 100644 (file)
@@ -43,7 +43,7 @@ class O2MCollectionTest(_fixtures.FixtureTest):
     def test_collection_move_hitslazy(self):
         User, Address = self.classes.User, self.classes.Address
 
-        sess = fixture_session()
+        sess = fixture_session(future=True)
         a1 = Address(email_address="address1")
         a2 = Address(email_address="address2")
         a3 = Address(email_address="address3")
@@ -667,7 +667,7 @@ class O2OScalarOrphanTest(_fixtures.FixtureTest):
     def test_m2o_event(self):
         User, Address = self.classes.User, self.classes.Address
 
-        sess = fixture_session()
+        sess = fixture_session(future=True)
         a1 = Address(email_address="address1")
         u1 = User(name="jack", address=a1)
 
@@ -678,6 +678,7 @@ class O2OScalarOrphanTest(_fixtures.FixtureTest):
         u2 = User(name="ed")
         # the _SingleParent extension sets the backref get to "active" !
         # u1 gets loaded and deleted
+        sess.add(u2)
         u2.address = a1
         sess.commit()
         assert sess.query(User).count() == 1
@@ -712,7 +713,7 @@ class M2MCollectionMoveTest(_fixtures.FixtureTest):
 
         Item, Keyword = (self.classes.Item, self.classes.Keyword)
 
-        session = fixture_session(autoflush=False)
+        session = fixture_session(autoflush=False, future=True)
 
         i1 = Item(description="i1")
         session.add(i1)
index 8749a0147363213c99c0cd542088bfb4335f5ac7..cd7e7c111a38ccb6397173e6b06c259a504e3237 100644 (file)
@@ -4485,7 +4485,15 @@ class ViewonlyFlagWarningTest(fixtures.MappedTest):
         )
 
 
-class CollectionCascadesDespiteBackrefTest(fixtures.TestBase):
+class CollectionCascadesNoBackrefTest(fixtures.TestBase):
+    """test the removal of cascade_backrefs behavior
+
+
+    see test/orm/test_deprecations.py::CollectionCascadesDespiteBackrefTest
+    for the deprecated version
+
+    """
+
     @testing.fixture
     def cascade_fixture(self, registry):
         def go(collection_class):
@@ -4495,7 +4503,10 @@ class CollectionCascadesDespiteBackrefTest(fixtures.TestBase):
 
                 id = Column(Integer, primary_key=True)
                 bs = relationship(
-                    "B", backref="a", collection_class=collection_class
+                    "B",
+                    backref="a",
+                    collection_class=collection_class,
+                    cascade_backrefs=False,
                 )
 
             @registry.mapped
@@ -4536,12 +4547,8 @@ class CollectionCascadesDespiteBackrefTest(fixtures.TestBase):
         b1.a = a1
         b3.a = a1
 
-        if future:
-            assert b1 not in s
-            assert b3 not in s
-        else:
-            assert b1 in s
-            assert b3 in s
+        assert b1 not in s
+        assert b3 not in s
 
         if methname == "__setitem__":
             meth = getattr(a1.bs, methname)
@@ -4563,8 +4570,4 @@ class CollectionCascadesDespiteBackrefTest(fixtures.TestBase):
         assert b1 in s
         assert b2 in s
 
-        if future:
-            assert b3 not in s  # the event never triggers from reverse
-        else:
-            # old behavior
-            assert b3 in s
+        assert b3 not in s  # the event never triggers from reverse
index 611754bdb3a451fd69ee2a1c2cf5eb05312e2bce..3eb50ebee94b0f76a718ac92828d6ff379413f53 100644 (file)
@@ -59,6 +59,7 @@ from sqlalchemy.orm import undefer
 from sqlalchemy.orm import with_loader_criteria
 from sqlalchemy.orm import with_parent
 from sqlalchemy.orm import with_polymorphic
+from sqlalchemy.orm.collections import attribute_mapped_collection
 from sqlalchemy.orm.collections import collection
 from sqlalchemy.orm.util import polymorphic_union
 from sqlalchemy.sql import elements
@@ -8456,3 +8457,249 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
             "FROM addresses WHERE :param_2 = addresses.user_id) AS anon_1",
             checkparams={"param_1": 7, "param_2": 8},
         )
+
+
+class CollectionCascadesDespiteBackrefTest(fixtures.TestBase):
+    """test old cascade_backrefs behavior
+
+    see test/orm/test_cascade.py::class CollectionCascadesNoBackrefTest
+    for the future version
+
+    """
+
+    @testing.fixture
+    def cascade_fixture(self, registry):
+        def go(collection_class):
+            @registry.mapped
+            class A(object):
+                __tablename__ = "a"
+
+                id = Column(Integer, primary_key=True)
+                bs = relationship(
+                    "B", backref="a", collection_class=collection_class
+                )
+
+            @registry.mapped
+            class B(object):
+                __tablename__ = "b_"
+                id = Column(Integer, primary_key=True)
+                a_id = Column(ForeignKey("a.id"))
+                key = Column(String)
+
+            return A, B
+
+        yield go
+
+    @testing.combinations(
+        (set, "add"),
+        (list, "append"),
+        (attribute_mapped_collection("key"), "__setitem__"),
+        (attribute_mapped_collection("key"), "setdefault"),
+        (attribute_mapped_collection("key"), "update_dict"),
+        (attribute_mapped_collection("key"), "update_kw"),
+        argnames="collection_class,methname",
+    )
+    @testing.combinations((True,), (False,), argnames="future")
+    def test_cascades_on_collection(
+        self, cascade_fixture, collection_class, methname, future
+    ):
+        A, B = cascade_fixture(collection_class)
+
+        s = Session(future=future)
+
+        a1 = A()
+        s.add(a1)
+
+        b1 = B(key="b1")
+        b2 = B(key="b2")
+        b3 = B(key="b3")
+
+        if future:
+            dep_ctx = util.nullcontext
+        else:
+
+            def dep_ctx():
+                return assertions.expect_deprecated_20(
+                    '"B" object is being merged into a Session along the '
+                    'backref cascade path for relationship "A.bs"'
+                )
+
+        with dep_ctx():
+            b1.a = a1
+        with dep_ctx():
+            b3.a = a1
+
+        if future:
+            assert b1 not in s
+            assert b3 not in s
+        else:
+            assert b1 in s
+            assert b3 in s
+
+        if methname == "__setitem__":
+            meth = getattr(a1.bs, methname)
+            meth(b1.key, b1)
+            meth(b2.key, b2)
+        elif methname == "setdefault":
+            meth = getattr(a1.bs, methname)
+            meth(b1.key, b1)
+            meth(b2.key, b2)
+        elif methname == "update_dict" and isinstance(a1.bs, dict):
+            a1.bs.update({b1.key: b1, b2.key: b2})
+        elif methname == "update_kw" and isinstance(a1.bs, dict):
+            a1.bs.update(b1=b1, b2=b2)
+        else:
+            meth = getattr(a1.bs, methname)
+            meth(b1)
+            meth(b2)
+
+        assert b1 in s
+        assert b2 in s
+
+        # future version:
+        if future:
+            assert b3 not in s  # the event never triggers from reverse
+        else:
+            # old behavior
+            assert b3 in s
+
+
+class LoadOnFKsTest(fixtures.DeclarativeMappedTest):
+    @classmethod
+    def setup_classes(cls):
+        Base = cls.DeclarativeBasic
+
+        class Parent(Base):
+            __tablename__ = "parent"
+            __table_args__ = {"mysql_engine": "InnoDB"}
+
+            id = Column(
+                Integer, primary_key=True, test_needs_autoincrement=True
+            )
+
+        class Child(Base):
+            __tablename__ = "child"
+            __table_args__ = {"mysql_engine": "InnoDB"}
+
+            id = Column(
+                Integer, primary_key=True, test_needs_autoincrement=True
+            )
+            parent_id = Column(Integer, ForeignKey("parent.id"))
+
+            parent = relationship(Parent, backref=backref("children"))
+
+    @testing.fixture
+    def parent_fixture(self, connection):
+        Parent, Child = self.classes("Parent", "Child")
+
+        sess = fixture_session(bind=connection, autoflush=False)
+        p1 = Parent()
+        p2 = Parent()
+        c1, c2 = Child(), Child()
+        c1.parent = p1
+        sess.add_all([p1, p2])
+        assert c1 in sess
+
+        yield sess, p1, p2, c1, c2
+
+        sess.close()
+
+    def test_enable_rel_loading_on_persistent_allows_backref_event(
+        self, parent_fixture
+    ):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
+        c3 = Child()
+        sess.enable_relationship_loading(c3)
+        c3.parent_id = p1.id
+        with assertions.expect_deprecated_20(
+            '"Child" object is being merged into a Session along the '
+            'backref cascade path for relationship "Parent.children"'
+        ):
+            c3.parent = p1
+
+        # backref fired off when c3.parent was set,
+        # because the "old" value was None
+        # change as of [ticket:3708]
+        assert c3 in p1.children
+
+    def test_enable_rel_loading_allows_backref_event(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
+        c3 = Child()
+        sess.enable_relationship_loading(c3)
+        c3.parent_id = p1.id
+
+        with assertions.expect_deprecated_20(
+            '"Child" object is being merged into a Session along the '
+            'backref cascade path for relationship "Parent.children"'
+        ):
+            c3.parent = p1
+
+        # backref fired off when c3.parent was set,
+        # because the "old" value was None
+        # change as of [ticket:3708]
+        assert c3 in p1.children
+
+
+class LazyTest(_fixtures.FixtureTest):
+    run_inserts = "once"
+    run_deletes = None
+
+    def test_backrefs_dont_lazyload(self):
+        users, Address, addresses, User = (
+            self.tables.users,
+            self.classes.Address,
+            self.tables.addresses,
+            self.classes.User,
+        )
+
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties={"addresses": relationship(Address, backref="user")},
+        )
+        self.mapper_registry.map_imperatively(Address, addresses)
+        sess = fixture_session(autoflush=False)
+        ad = sess.query(Address).filter_by(id=1).one()
+        assert ad.user.id == 7
+
+        def go():
+            ad.user = None
+            assert ad.user is None
+
+        self.assert_sql_count(testing.db, go, 0)
+
+        u1 = sess.query(User).filter_by(id=7).one()
+
+        def go():
+            assert ad not in u1.addresses
+
+        self.assert_sql_count(testing.db, go, 1)
+
+        sess.expire(u1, ["addresses"])
+
+        def go():
+            assert ad in u1.addresses
+
+        self.assert_sql_count(testing.db, go, 1)
+
+        sess.expire(u1, ["addresses"])
+        ad2 = Address()
+
+        def go():
+            with assertions.expect_deprecated_20(
+                ".* object is being merged into a Session along the "
+                "backref cascade path for relationship "
+            ):
+                ad2.user = u1
+            assert ad2.user is u1
+
+        self.assert_sql_count(testing.db, go, 0)
+
+        def go():
+            assert ad2 in u1.addresses
+
+        self.assert_sql_count(testing.db, go, 1)
index d9204db96c84c84f8290cfaa3c8c512ea867789b..411e09d00e8c820daab6d74ca5736e52136ec7c4 100644 (file)
@@ -1683,7 +1683,7 @@ class ExpiredPendingTest(_fixtures.FixtureTest):
         )
         self.mapper_registry.map_imperatively(Address, addresses)
 
-        sess = fixture_session(autoflush=False)
+        sess = fixture_session(autoflush=False, future=True)
         a1 = Address(email_address="a1")
         sess.add(a1)
         sess.flush()
@@ -1701,6 +1701,9 @@ class ExpiredPendingTest(_fixtures.FixtureTest):
         a2 = Address(email_address="a2")
         a2.user = u1
 
+        # needed now that cascade backrefs is disabled
+        sess.add(a2)
+
         # expire u1.addresses again.  this expires
         # "pending" as well.
         sess.expire(u1, ["addresses"])
index 412637ec6a519e6c4f6a8f1ccbdf1b70d5ddd795..295c9049cc7520746ff37bb4840aa7d26c954091 100644 (file)
@@ -954,7 +954,7 @@ class LazyTest(_fixtures.FixtureTest):
             properties={"addresses": relationship(Address, backref="user")},
         )
         self.mapper_registry.map_imperatively(Address, addresses)
-        sess = fixture_session(autoflush=False)
+        sess = fixture_session(autoflush=False, future=True)
         ad = sess.query(Address).filter_by(id=1).one()
         assert ad.user.id == 7
 
index 02de9b2bb4389d1195e5821d21c0e60249b384d5..fda8be423685d9bfa3535a385b36d3c3158bb510 100644 (file)
@@ -5,7 +5,6 @@ from sqlalchemy import testing
 from sqlalchemy.orm import backref
 from sqlalchemy.orm import declarative_base
 from sqlalchemy.orm import relationship
-from sqlalchemy.orm import Session
 from sqlalchemy.orm.attributes import instance_state
 from sqlalchemy.testing import AssertsExecutionResults
 from sqlalchemy.testing import fixtures
@@ -65,12 +64,10 @@ class FlushOnPendingTest(AssertsExecutionResults, fixtures.TestBase):
         self.assert_sql_count(testing.db, go, 0)
 
 
-class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
-    __leave_connections_for_teardown__ = True
-
-    def setup_test(self):
-        global Parent, Child, Base
-        Base = declarative_base()
+class LoadOnFKsTest(fixtures.DeclarativeMappedTest):
+    @classmethod
+    def setup_classes(cls):
+        Base = cls.DeclarativeBasic
 
         class Parent(Base):
             __tablename__ = "parent"
@@ -91,11 +88,11 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
 
             parent = relationship(Parent, backref=backref("children"))
 
-        Base.metadata.create_all(testing.db)
-
-        global sess, p1, p2, c1, c2
-        sess = Session(bind=testing.db)
+    @testing.fixture
+    def parent_fixture(self, connection):
+        Parent, Child = self.classes("Parent", "Child")
 
+        sess = fixture_session(bind=connection)
         p1 = Parent()
         p2 = Parent()
         c1, c2 = Child(), Child()
@@ -103,38 +100,22 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
         sess.add_all([p1, p2])
         assert c1 in sess
 
-        sess.commit()
+        sess.flush()
 
-    def teardown_test(self):
-        sess.rollback()
-        Base.metadata.drop_all(testing.db)
+        Child.parent.property.load_on_pending = False
 
-    def test_load_on_pending_allows_backref_event(self):
-        Child.parent.property.load_on_pending = True
-        sess.autoflush = False
-        c3 = Child()
-        sess.add(c3)
-        c3.parent_id = p1.id
-        c3.parent = p1
+        sess.expire_all()
 
-        # backref fired off when c3.parent was set,
-        # because the "old" value was None.
-        # change as of [ticket:3708]
-        assert c3 in p1.children
+        yield sess, p1, p2, c1, c2
 
-    def test_enable_rel_loading_allows_backref_event(self):
-        sess.autoflush = False
-        c3 = Child()
-        sess.enable_relationship_loading(c3)
-        c3.parent_id = p1.id
-        c3.parent = p1
+        sess.close()
 
-        # backref fired off when c3.parent was set,
-        # because the "old" value was None
-        # change as of [ticket:3708]
-        assert c3 in p1.children
+    def test_m2o_history_on_persistent_allows_backref_event(
+        self, parent_fixture
+    ):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
 
-    def test_m2o_history_on_persistent_allows_backref_event(self):
         c3 = Child()
         sess.add(c3)
         c3.parent_id = p1.id
@@ -142,7 +123,10 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
 
         assert c3 in p1.children
 
-    def test_load_on_persistent_allows_backref_event(self):
+    def test_load_on_persistent_allows_backref_event(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
         Child.parent.property.load_on_pending = True
         c3 = Child()
         sess.add(c3)
@@ -151,18 +135,28 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
 
         assert c3 in p1.children
 
-    def test_enable_rel_loading_on_persistent_allows_backref_event(self):
+    def test_load_on_pending_allows_backref_event(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
+        sess.autoflush = False
+
+        Child.parent.property.load_on_pending = True
         c3 = Child()
-        sess.enable_relationship_loading(c3)
+        sess.add(c3)
         c3.parent_id = p1.id
+
         c3.parent = p1
 
         # backref fired off when c3.parent was set,
-        # because the "old" value was None
+        # because the "old" value was None.
         # change as of [ticket:3708]
         assert c3 in p1.children
 
-    def test_no_load_on_pending_allows_backref_event(self):
+    def test_no_load_on_pending_allows_backref_event(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
         # users who stick with the program and don't use
         # 'load_on_pending' get expected behavior
 
@@ -175,7 +169,10 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
 
         assert c3 in p1.children
 
-    def test_autoflush_on_pending(self):
+    def test_autoflush_on_pending(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
         # ensure p1.id is not expired
         p1.id
 
@@ -186,7 +183,10 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
         # pendings don't autoflush
         assert c3.parent is None
 
-    def test_autoflush_load_on_pending_on_pending(self):
+    def test_autoflush_load_on_pending_on_pending(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
         # ensure p1.id is not expired
         p1.id
 
@@ -198,7 +198,10 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
         # ...unless the flag is on
         assert c3.parent is p1
 
-    def test_collection_load_from_pending_populated(self):
+    def test_collection_load_from_pending_populated(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
         Parent.children.property.load_on_pending = True
         p2 = Parent(id=p1.id)
         sess.add(p2)
@@ -209,7 +212,10 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
 
         self.assert_sql_count(testing.db, go, 1)
 
-    def test_collection_load_from_pending_no_sql(self):
+    def test_collection_load_from_pending_no_sql(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
         Parent.children.property.load_on_pending = True
         p2 = Parent(id=None)
         sess.add(p2)
@@ -221,7 +227,10 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
 
         self.assert_sql_count(testing.db, go, 0)
 
-    def test_load_on_pending_with_set(self):
+    def test_load_on_pending_with_set(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
         Child.parent.property.load_on_pending = True
 
         p1.children
@@ -236,7 +245,10 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
 
         self.assert_sql_count(testing.db, go, 0)
 
-    def test_backref_doesnt_double(self):
+    def test_backref_doesnt_double(self, parent_fixture):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
         Child.parent.property.load_on_pending = True
         sess.autoflush = False
         p1.children
@@ -248,116 +260,133 @@ class LoadOnFKsTest(AssertsExecutionResults, fixtures.TestBase):
         c3.parent = p1
         assert len(p1.children) == 2
 
-    def test_m2o_lazy_loader_on_persistent(self):
+    @testing.combinations(True, False, argnames="loadfk")
+    @testing.combinations(True, False, argnames="loadrel")
+    @testing.combinations(True, False, argnames="autoflush")
+    @testing.combinations(True, False, argnames="manualflush")
+    @testing.combinations(True, False, argnames="fake_autoexpire")
+    def test_m2o_lazy_loader_on_persistent(
+        self,
+        parent_fixture,
+        loadfk,
+        loadrel,
+        autoflush,
+        manualflush,
+        fake_autoexpire,
+    ):
         """Compare the behaviors from the lazyloader using
         the "committed" state in all cases, vs. the lazyloader
         using the "current" state in all cases except during flush.
 
         """
 
-        for loadfk in (True, False):
-            for loadrel in (True, False):
-                for autoflush in (True, False):
-                    for manualflush in (True, False):
-                        for fake_autoexpire in (True, False):
-                            sess.autoflush = autoflush
-
-                            if loadfk:
-                                c1.parent_id
-                            if loadrel:
-                                c1.parent
-
-                            c1.parent_id = p2.id
-
-                            if manualflush:
-                                sess.flush()
-
-                            # fake_autoexpire refers to the eventual
-                            # auto-expire of 'parent' when c1.parent_id
-                            # is altered.
-                            if fake_autoexpire:
-                                sess.expire(c1, ["parent"])
-
-                            # old 0.6 behavior
-                            # if manualflush and (not loadrel or
-                            #                     fake_autoexpire):
-                            #    # a flush occurs, we get p2
-                            #    assert c1.parent is p2
-                            # elif not loadrel and not loadfk:
-                            #    # problematically - we get None since
-                            #    # committed state
-                            #    # is empty when c1.parent_id was mutated,
-                            #    # since we want
-                            #    # to save on selects.  this is
-                            #    # why the patch goes in in 0.6 - this is
-                            #    # mostly a bug.
-                            #    assert c1.parent is None
-                            # else:
-                            #    # if things were loaded, autoflush doesn't
-                            #    # even happen.
-                            #    assert c1.parent is p1
-
-                            # new behavior
-                            if loadrel and not fake_autoexpire:
-                                assert c1.parent is p1
-                            else:
-                                assert c1.parent is p2
-
-                            sess.rollback()
-
-    def test_m2o_lazy_loader_on_pending(self):
-        for loadonpending in (False, True):
-            for autoflush in (False, True):
-                for manualflush in (False, True):
-                    Child.parent.property.load_on_pending = loadonpending
-                    sess.autoflush = autoflush
-
-                    # ensure p2.id not expired
-                    p2.id
-
-                    c2 = Child()
-                    sess.add(c2)
-                    c2.parent_id = p2.id
-
-                    if manualflush:
-                        sess.flush()
-
-                    if loadonpending or manualflush:
-                        assert c2.parent is p2
-                    else:
-                        assert c2.parent is None
-
-                    sess.rollback()
-
-    def test_m2o_lazy_loader_on_transient(self):
-        for loadonpending in (False, True):
-            for attach in (False, True):
-                for autoflush in (False, True):
-                    for manualflush in (False, True):
-                        for enable_relationship_rel in (False, True):
-                            Child.parent.property.load_on_pending = (
-                                loadonpending
-                            )
-                            sess.autoflush = autoflush
-                            c2 = Child()
-
-                            if attach:
-                                state = instance_state(c2)
-                                state.session_id = sess.hash_key
-
-                            if enable_relationship_rel:
-                                sess.enable_relationship_loading(c2)
-
-                            c2.parent_id = p2.id
-
-                            if manualflush:
-                                sess.flush()
-
-                            if (
-                                loadonpending and attach
-                            ) or enable_relationship_rel:
-                                assert c2.parent is p2
-                            else:
-                                assert c2.parent is None
-
-                            sess.rollback()
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
+        sess.autoflush = autoflush
+
+        if loadfk:
+            c1.parent_id
+        if loadrel:
+            c1.parent
+
+        c1.parent_id = p2.id
+
+        if manualflush:
+            sess.flush()
+
+        # fake_autoexpire refers to the eventual
+        # auto-expire of 'parent' when c1.parent_id
+        # is altered.
+        if fake_autoexpire:
+            sess.expire(c1, ["parent"])
+
+        # old 0.6 behavior
+        # if manualflush and (not loadrel or
+        #                     fake_autoexpire):
+        #    # a flush occurs, we get p2
+        #    assert c1.parent is p2
+        # elif not loadrel and not loadfk:
+        #    # problematically - we get None since
+        #    # committed state
+        #    # is empty when c1.parent_id was mutated,
+        #    # since we want
+        #    # to save on selects.  this is
+        #    # why the patch goes in in 0.6 - this is
+        #    # mostly a bug.
+        #    assert c1.parent is None
+        # else:
+        #    # if things were loaded, autoflush doesn't
+        #    # even happen.
+        #    assert c1.parent is p1
+
+        # new behavior
+        if loadrel and not fake_autoexpire:
+            assert c1.parent is p1
+        else:
+            assert c1.parent is p2
+
+    @testing.combinations(True, False, argnames="loadonpending")
+    @testing.combinations(True, False, argnames="autoflush")
+    @testing.combinations(True, False, argnames="manualflush")
+    def test_m2o_lazy_loader_on_pending(
+        self, parent_fixture, loadonpending, autoflush, manualflush
+    ):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
+        Child.parent.property.load_on_pending = loadonpending
+        sess.autoflush = autoflush
+
+        # ensure p2.id not expired
+        p2.id
+
+        c2 = Child()
+        sess.add(c2)
+        c2.parent_id = p2.id
+
+        if manualflush:
+            sess.flush()
+
+        if loadonpending or manualflush:
+            assert c2.parent is p2
+        else:
+            assert c2.parent is None
+
+    @testing.combinations(True, False, argnames="loadonpending")
+    @testing.combinations(True, False, argnames="attach")
+    @testing.combinations(True, False, argnames="autoflush")
+    @testing.combinations(True, False, argnames="manualflush")
+    @testing.combinations(True, False, argnames="enable_relationship_rel")
+    def test_m2o_lazy_loader_on_transient(
+        self,
+        parent_fixture,
+        loadonpending,
+        attach,
+        autoflush,
+        manualflush,
+        enable_relationship_rel,
+    ):
+        sess, p1, p2, c1, c2 = parent_fixture
+        Parent, Child = self.classes("Parent", "Child")
+
+        Child.parent.property.load_on_pending = loadonpending
+        sess.autoflush = autoflush
+        c2 = Child()
+
+        if attach:
+            state = instance_state(c2)
+            state.session_id = sess.hash_key
+
+        if enable_relationship_rel:
+            sess.enable_relationship_loading(c2)
+
+        c2.parent_id = p2.id
+
+        if manualflush:
+            sess.flush()
+
+        if (loadonpending and attach) or enable_relationship_rel:
+            assert c2.parent is p2
+        else:
+            assert c2.parent is None
index 7d9e6f9c38a479ca2a5ecc62efb7c53652833866..cbc164ff3da6cc9242c6108a657b71d32478cd3b 100644 (file)
@@ -2349,7 +2349,7 @@ class RequirementsTest(fixtures.MappedTest):
         self.mapper(H3, ht3)
         self.mapper(H6, ht6)
 
-        s = fixture_session()
+        s = fixture_session(future=True)
         s.add_all([H1("abc"), H1("def")])
         h1 = H1("ghi")
         s.add(h1)
@@ -2367,7 +2367,7 @@ class RequirementsTest(fixtures.MappedTest):
         h6 = H6()
         h6.h1a = h1
         h6.h1b = x = H1()
-        assert x in s
+        s.add(x)
 
         h6.h1b.h2s.append(H2("def"))
 
index 3b97bd5a579e5429751c16bb57a4c95a31085312..034339b29d39a9c309b356ae8923244ff36afa5f 100644 (file)
@@ -1432,7 +1432,7 @@ class MergeTest(_fixtures.FixtureTest):
             self.tables.users,
         )
 
-        s = fixture_session(autoflush=True, autocommit=False)
+        s = fixture_session(autoflush=True, autocommit=False, future=True)
         self.mapper_registry.map_imperatively(
             User,
             users,
@@ -1445,8 +1445,10 @@ class MergeTest(_fixtures.FixtureTest):
         )
 
         a1 = Address(user=s.merge(User(id=1, name="ed")), email_address="x")
+        s.add(a1)
         before_id = id(a1.user)
         a2 = Address(user=s.merge(User(id=1, name="jack")), email_address="x")
+        s.add(a2)
         after_id = id(a1.user)
         other_id = id(a2.user)
         eq_(before_id, other_id)
index fba335d285a131030a62af1027c40be0f54048c1..8f9e36620807c6a43875396ca5aed86dea09bf42 100644 (file)
@@ -926,7 +926,7 @@ class SelfReferentialTest(fixtures.MappedTest):
             },
         )
 
-        sess = fixture_session()
+        sess = fixture_session(future=True)
         n1 = Node(name="n1")
         sess.add(n1)
         n2 = Node(name="n11", parentnode=n1)
index 94b35c5b3f79b4a95cc9c671faa46f1c1cadde36..bd1f9545a46e64f7181363d30a7e6a317025da7f 100644 (file)
@@ -922,7 +922,7 @@ class SessionStateTest(_fixtures.FixtureTest):
         )
         self.mapper_registry.map_imperatively(Address, addresses)
 
-        session = fixture_session()
+        session = fixture_session(future=True)
 
         @event.listens_for(session, "after_flush")
         def load_collections(session, flush_context):
@@ -943,6 +943,9 @@ class SessionStateTest(_fixtures.FixtureTest):
         assert "addresses" not in inspect(u1).dict
         assert a2 in inspect(u1)._pending_mutations["addresses"].added_items
 
+        # this is needed now that cascade_backrefs is turned off
+        session.add(a2)
+
         with assertions.expect_warnings(
             r"Identity map already had an identity "
             r"for \(.*Address.*\), replacing"
index 57fe0b7a2c7e7b6a243da5a8e63d23f05abd9c8d..c995f9771502d8ccdcaf61e8a1776e08d77c41b9 100644 (file)
@@ -828,7 +828,7 @@ class NoBumpOnRelationshipTest(fixtures.MappedTest):
 
     def _run_test(self, auto_version_counter=True):
         A, B = self.classes("A", "B")
-        s = fixture_session()
+        s = fixture_session(future=True)
         if auto_version_counter:
             a1 = A()
         else: