diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-02-26 16:51:32 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-02-27 15:55:06 -0500 |
commit | e5e5bb640abc5c98b39a6a3a955a20ef1525fc02 (patch) | |
tree | d0e6035f32930018606efe8ca75c1d285bdf834c | |
parent | f78db5e1f68d6b2fb6a7acc04036f682d9a22974 (diff) | |
download | sqlalchemy-e5e5bb640abc5c98b39a6a3a955a20ef1525fc02.tar.gz |
Open up check for relationships that write to the same column
Enhanced logic that tracks if relationships will be conflicting with each
other when they write to the same column to include simple cases of two
relationships that should have a "backref" between them. This means that
if two relationships are not viewonly, are not linked with back_populates
and are not otherwise in an inheriting sibling/overriding arrangement, and
will populate the same foreign key column, a warning is emitted at mapper
configuration time warning that a conflict may arise. A new parameter
:paramref:`.relationship.overlaps` is added to suit those very rare cases
where such an overlapping persistence arrangement may be unavoidable.
Fixes: #5171
Change-Id: Ifae5998fc1c7e49ce059aec8a67c80cabee768ad
-rw-r--r-- | doc/build/changelog/unreleased_14/5171.rst | 14 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 11 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/relationships.py | 45 | ||||
-rw-r--r-- | test/ext/declarative/test_inheritance.py | 2 | ||||
-rw-r--r-- | test/ext/test_associationproxy.py | 15 | ||||
-rw-r--r-- | test/orm/inheritance/test_basic.py | 4 | ||||
-rw-r--r-- | test/orm/inheritance/test_single.py | 4 | ||||
-rw-r--r-- | test/orm/test_cycles.py | 5 | ||||
-rw-r--r-- | test/orm/test_deferred.py | 4 | ||||
-rw-r--r-- | test/orm/test_eager_relations.py | 7 | ||||
-rw-r--r-- | test/orm/test_froms.py | 2 | ||||
-rw-r--r-- | test/orm/test_instrumentation.py | 4 | ||||
-rw-r--r-- | test/orm/test_lazy_relations.py | 2 | ||||
-rw-r--r-- | test/orm/test_options.py | 10 | ||||
-rw-r--r-- | test/orm/test_query.py | 1 | ||||
-rw-r--r-- | test/orm/test_relationships.py | 146 | ||||
-rw-r--r-- | test/orm/test_selectin_relations.py | 16 | ||||
-rw-r--r-- | test/orm/test_subquery_relations.py | 4 | ||||
-rw-r--r-- | test/orm/test_unitofwork.py | 2 |
19 files changed, 268 insertions, 30 deletions
diff --git a/doc/build/changelog/unreleased_14/5171.rst b/doc/build/changelog/unreleased_14/5171.rst new file mode 100644 index 000000000..65824a0a6 --- /dev/null +++ b/doc/build/changelog/unreleased_14/5171.rst @@ -0,0 +1,14 @@ +.. change:: + :tags: usecase, orm + :tickets: 5171 + + Enhanced logic that tracks if relationships will be conflicting with each + other when they write to the same column to include simple cases of two + relationships that should have a "backref" between them. This means that + if two relationships are not viewonly, are not linked with back_populates + and are not otherwise in an inheriting sibling/overriding arrangement, and + will populate the same foreign key column, a warning is emitted at mapper + configuration time warning that a conflict may arise. A new parameter + :paramref:`.relationship.overlaps` is added to suit those very rare cases + where such an overlapping persistence arrangement may be unavoidable. + diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index b84d41260..0d87a9c40 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2557,6 +2557,17 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr): return self.base_mapper is other.base_mapper + def is_sibling(self, other): + """return true if the other mapper is an inheriting sibling to this + one. common parent but different branch + + """ + return ( + self.base_mapper is other.base_mapper + and not self.isa(other) + and not other.isa(self) + ) + def _canload(self, state, allow_subtypes): s = self.primary_mapper() if self.polymorphic_on is not None or allow_subtypes: diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index 5573f7c9a..b82a3d271 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -16,6 +16,7 @@ and `secondaryjoin` aspects of :func:`.relationship`. from __future__ import absolute_import import collections +import re import weakref from . import attributes @@ -131,6 +132,7 @@ class RelationshipProperty(StrategizedProperty): order_by=False, backref=None, back_populates=None, + overlaps=None, post_update=False, cascade=False, viewonly=False, @@ -320,6 +322,18 @@ class RelationshipProperty(StrategizedProperty): :paramref:`~.relationship.backref` - alternative form of backref specification. + :param overlaps: + A string name or comma-delimited set of names of other relationships + on either this mapper, a descendant mapper, or a target mapper with + which this relationship may write to the same foreign keys upon + persistence. The only effect this has is to eliminate the + warning that this relationship will conflict with another upon + persistence. This is used for such relationships that are truly + capable of conflicting with each other on write, but the application + will ensure that no such conflicts occur. + + .. versionadded:: 1.4 + :param bake_queries=True: Use the :class:`.BakedQuery` cache to cache the construction of SQL used in lazy loads. True by default. Set to False if the @@ -916,6 +930,10 @@ class RelationshipProperty(StrategizedProperty): self.strategy_key = (("lazy", self.lazy),) self._reverse_property = set() + if overlaps: + self._overlaps = set(re.split(r"\s*,\s*", overlaps)) + else: + self._overlaps = () if cascade is not False: self.cascade = cascade @@ -3120,8 +3138,6 @@ class JoinCondition(object): # if multiple relationships overlap foreign() directly, but # we're going to assume it's typically a ForeignKeyConstraint- # level configuration that benefits from this warning. - if len(to_.foreign_keys) < 2: - continue if to_ not in self._track_overlapping_sync_targets: self._track_overlapping_sync_targets[ @@ -3134,12 +3150,15 @@ class JoinCondition(object): for pr, fr_ in prop_to_from.items(): if ( pr.mapper in mapperlib._mapper_registry + and pr not in self.prop._reverse_property + and pr.key not in self.prop._overlaps + and self.prop.key not in pr._overlaps + and not self.prop.parent.is_sibling(pr.parent) + and not self.prop.mapper.is_sibling(pr.mapper) and ( - self.prop._persists_for(pr.parent) - or pr._persists_for(self.prop.parent) + self.prop.key != pr.key + or not self.prop.parent.common_parent(pr.parent) ) - and fr_ is not from_ - and pr not in self.prop._reverse_property ): other_props.append((pr, fr_)) @@ -3148,10 +3167,16 @@ class JoinCondition(object): util.warn( "relationship '%s' will copy column %s to column %s, " "which conflicts with relationship(s): %s. " - "Consider applying " - "viewonly=True to read-only relationships, or provide " - "a primaryjoin condition marking writable columns " - "with the foreign() annotation." + "If this is not the intention, consider if these " + "relationships should be linked with " + "back_populates, or if viewonly=True should be " + "applied to one or more if they are read-only. " + "For the less common case that foreign key " + "constraints are partially overlapping, the " + "orm.foreign() " + "annotation can be used to isolate the columns that " + "should be written towards. The 'overlaps' " + "parameter may be used to remove this warning." % ( self.prop, from_, diff --git a/test/ext/declarative/test_inheritance.py b/test/ext/declarative/test_inheritance.py index 083fdb0db..d33dbd4be 100644 --- a/test/ext/declarative/test_inheritance.py +++ b/test/ext/declarative/test_inheritance.py @@ -1917,7 +1917,7 @@ class ConcreteExtensionConfigTest( @declared_attr def something_else(cls): counter(cls, "something_else") - return relationship("Something") + return relationship("Something", viewonly=True) class ConcreteConcreteAbstraction(AbstractConcreteAbstraction): __tablename__ = "cca" diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py index e7cc6251b..ddca2f78e 100644 --- a/test/ext/test_associationproxy.py +++ b/test/ext/test_associationproxy.py @@ -101,6 +101,9 @@ class AutoFlushTest(fixtures.TablesTest): Column("name", String(50)), ) + def teardown(self): + clear_mappers() + def _fixture(self, collection_class, is_dict=False): class Parent(object): collection = association_proxy("_collection", "child") @@ -1596,8 +1599,10 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL): Keyword, keywords, properties={ - "user_keyword": relationship(UserKeyword, uselist=False), - "user_keywords": relationship(UserKeyword), + "user_keyword": relationship( + UserKeyword, uselist=False, back_populates="keyword" + ), + "user_keywords": relationship(UserKeyword, viewonly=True), }, ) @@ -1606,7 +1611,9 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL): userkeywords, properties={ "user": relationship(User, backref="user_keywords"), - "keyword": relationship(Keyword), + "keyword": relationship( + Keyword, back_populates="user_keyword" + ), }, ) mapper( @@ -3426,7 +3433,7 @@ class ScopeBehaviorTest(fixtures.DeclarativeMappedTest): data = Column(String(50)) bs = relationship("B") - b_dyn = relationship("B", lazy="dynamic") + b_dyn = relationship("B", lazy="dynamic", viewonly=True) b_data = association_proxy("bs", "data") diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index 831781330..9b896b59a 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -1229,7 +1229,9 @@ class EagerLazyTest(fixtures.MappedTest): foos = mapper(Foo, foo) bars = mapper(Bar, bar, inherits=foos) bars.add_property("lazy", relationship(foos, bar_foo, lazy="select")) - bars.add_property("eager", relationship(foos, bar_foo, lazy="joined")) + bars.add_property( + "eager", relationship(foos, bar_foo, lazy="joined", viewonly=True) + ) foo.insert().execute(data="foo1") bar.insert().execute(id=1, data="bar1") diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index 9426847ba..3f3718190 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -1142,7 +1142,9 @@ class RelationshipToSingleTest( mapper( Company, companies, - properties={"engineers": relationship(Engineer)}, + properties={ + "engineers": relationship(Engineer, back_populates="company") + }, ) mapper( Employee, diff --git a/test/orm/test_cycles.py b/test/orm/test_cycles.py index 7dd349a74..22a26e617 100644 --- a/test/orm/test_cycles.py +++ b/test/orm/test_cycles.py @@ -71,13 +71,16 @@ class SelfReferentialTest(fixtures.MappedTest): C1, t1, properties={ - "c1s": relationship(C1, cascade="all"), + "c1s": relationship( + C1, cascade="all", back_populates="parent" + ), "parent": relationship( C1, primaryjoin=t1.c.parent_c1 == t1.c.c1, remote_side=t1.c.c1, lazy="select", uselist=False, + back_populates="c1s", ), }, ) diff --git a/test/orm/test_deferred.py b/test/orm/test_deferred.py index 5acfa3f79..9226580ea 100644 --- a/test/orm/test_deferred.py +++ b/test/orm/test_deferred.py @@ -1296,7 +1296,9 @@ class InheritanceTest(_Polymorphic): super(InheritanceTest, cls).setup_mappers() from sqlalchemy import inspect - inspect(Company).add_property("managers", relationship(Manager)) + inspect(Company).add_property( + "managers", relationship(Manager, viewonly=True) + ) def test_load_only_subclass(self): s = Session() diff --git a/test/orm/test_eager_relations.py b/test/orm/test_eager_relations.py index bf39b25a6..0a97c5246 100644 --- a/test/orm/test_eager_relations.py +++ b/test/orm/test_eager_relations.py @@ -771,6 +771,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="joined", order_by=open_mapper.id, + viewonly=True, ), closed_orders=relationship( closed_mapper, @@ -780,6 +781,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="joined", order_by=closed_mapper.id, + viewonly=True, ), ), ) @@ -906,6 +908,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="joined", order_by=orders.c.id, + viewonly=True, ), closed_orders=relationship( Order, @@ -914,9 +917,11 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="joined", order_by=orders.c.id, + viewonly=True, ), ), ) + self._run_double_test() def _run_double_test(self, no_items=False): @@ -3119,7 +3124,7 @@ class InnerJoinSplicingTest(fixtures.MappedTest, testing.AssertsCompiledSQL): b_np = aliased(B, weird_selectable, flat=True) a_mapper = inspect(A) - a_mapper.add_property("bs_np", relationship(b_np)) + a_mapper.add_property("bs_np", relationship(b_np, viewonly=True)) s = Session() diff --git a/test/orm/test_froms.py b/test/orm/test_froms.py index 08b68232b..2425cd756 100644 --- a/test/orm/test_froms.py +++ b/test/orm/test_froms.py @@ -3123,6 +3123,7 @@ class CustomJoinTest(QueryTest): orders.c.isopen == 1, users.c.id == orders.c.user_id ), lazy="select", + viewonly=True, ), closed_orders=relationship( Order, @@ -3130,6 +3131,7 @@ class CustomJoinTest(QueryTest): orders.c.isopen == 0, users.c.id == orders.c.user_id ), lazy="select", + viewonly=True, ), ), ) diff --git a/test/orm/test_instrumentation.py b/test/orm/test_instrumentation.py index ddf735e4f..16ccff936 100644 --- a/test/orm/test_instrumentation.py +++ b/test/orm/test_instrumentation.py @@ -6,6 +6,7 @@ from sqlalchemy import MetaData from sqlalchemy import util from sqlalchemy.orm import attributes from sqlalchemy.orm import class_mapper +from sqlalchemy.orm import clear_mappers from sqlalchemy.orm import create_session from sqlalchemy.orm import instrumentation from sqlalchemy.orm import mapper @@ -791,6 +792,8 @@ class MiscTest(fixtures.ORMTest): session.add(b) assert a in session, "base is %s" % base + clear_mappers() + def test_compileonattr_rel_backref_b(self): m = MetaData() t1 = Table( @@ -832,3 +835,4 @@ class MiscTest(fixtures.ORMTest): session = create_session() session.add(a) assert b in session, "base: %s" % base + clear_mappers() diff --git a/test/orm/test_lazy_relations.py b/test/orm/test_lazy_relations.py index ca7a58024..1c2720898 100644 --- a/test/orm/test_lazy_relations.py +++ b/test/orm/test_lazy_relations.py @@ -645,6 +645,7 @@ class LazyTest(_fixtures.FixtureTest): users.c.id == open_mapper.user_id, ), lazy="select", + overlaps="closed_orders", ), closed_orders=relationship( closed_mapper, @@ -653,6 +654,7 @@ class LazyTest(_fixtures.FixtureTest): users.c.id == closed_mapper.user_id, ), lazy="select", + overlaps="open_orders", ), ), ) diff --git a/test/orm/test_options.py b/test/orm/test_options.py index 97c00b3c6..00e1d232b 100644 --- a/test/orm/test_options.py +++ b/test/orm/test_options.py @@ -242,7 +242,7 @@ class OfTypePathingTest(PathTest, QueryTest): inherits=Address, properties={ "sub_attr": column_property(address_table.c.email_address), - "dings": relationship(Dingaling), + "dings": relationship(Dingaling, viewonly=True), }, ) @@ -585,7 +585,7 @@ class OptionsTest(PathTest, QueryTest): mapper( SubAddr, inherits=Address, - properties={"flub": relationship(Dingaling)}, + properties={"flub": relationship(Dingaling, viewonly=True)}, ) q = sess.query(Address) @@ -604,7 +604,7 @@ class OptionsTest(PathTest, QueryTest): mapper( SubAddr, inherits=Address, - properties={"flub": relationship(Dingaling)}, + properties={"flub": relationship(Dingaling, viewonly=True)}, ) q = sess.query(SubAddr) @@ -623,7 +623,7 @@ class OptionsTest(PathTest, QueryTest): mapper( SubAddr, inherits=Address, - properties={"flub": relationship(Dingaling)}, + properties={"flub": relationship(Dingaling, viewonly=True)}, ) q = sess.query(Address) @@ -708,7 +708,7 @@ class OptionsTest(PathTest, QueryTest): mapper( SubAddr, inherits=Address, - properties={"flub": relationship(Dingaling)}, + properties={"flub": relationship(Dingaling, viewonly=True)}, ) q = sess.query(User) diff --git a/test/orm/test_query.py b/test/orm/test_query.py index aabee82ad..2fb79604a 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -5083,6 +5083,7 @@ class WithTransientOnNone(_fixtures.FixtureTest, AssertsCompiledSQL): users.c.id == addresses.c.user_id, users.c.name == addresses.c.email_address, ), + viewonly=True, ), }, ) diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py index 78e9d77e8..53295c688 100644 --- a/test/orm/test_relationships.py +++ b/test/orm/test_relationships.py @@ -13,6 +13,7 @@ from sqlalchemy import select from sqlalchemy import String from sqlalchemy import testing from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import aliased from sqlalchemy.orm import attributes from sqlalchemy.orm import backref from sqlalchemy.orm import clear_mappers @@ -649,6 +650,7 @@ class OverlappingFksSiblingTest(fixtures.TestBase): add_b_amember=False, add_bsub1_a=False, add_bsub2_a_viewonly=False, + add_b_a_overlaps=None, ): Base = declarative_base(metadata=self.metadata) @@ -689,7 +691,9 @@ class OverlappingFksSiblingTest(fixtures.TestBase): # writes to B.a_id, which conflicts with BSub2.a_member, # so should warn if add_b_a: - a = relationship("A", viewonly=add_b_a_viewonly) + a = relationship( + "A", viewonly=add_b_a_viewonly, overlaps=add_b_a_overlaps + ) # if added, this relationship writes to B.a_id, which conflicts # with BSub1.a @@ -719,6 +723,88 @@ class OverlappingFksSiblingTest(fixtures.TestBase): return A, AMember, B, BSub1, BSub2 + def _fixture_two(self, setup_backrefs=False, setup_overlaps=False): + + Base = declarative_base(metadata=self.metadata) + + # purposely using the comma to make sure parsing the comma works + + class Parent(Base): + __tablename__ = "parent" + id = Column(Integer, primary_key=True) + children = relationship( + "Child", + back_populates=("parent" if setup_backrefs else None), + overlaps="foo, bar, parent" if setup_overlaps else None, + ) + + class Child(Base): + __tablename__ = "child" + id = Column(Integer, primary_key=True) + num = Column(Integer) + parent_id = Column( + Integer, ForeignKey("parent.id"), nullable=False + ) + parent = relationship( + "Parent", + back_populates=("children" if setup_backrefs else None), + overlaps="bar, bat, children" if setup_overlaps else None, + ) + + configure_mappers() + + def _fixture_three(self, use_same_mappers, setup_overlaps): + Base = declarative_base(metadata=self.metadata) + + class Child(Base): + __tablename__ = "child" + id = Column(Integer, primary_key=True) + num = Column(Integer) + parent_id = Column( + Integer, ForeignKey("parent.id"), nullable=False + ) + + if not use_same_mappers: + c1 = aliased(Child) + c2 = aliased(Child) + + class Parent(Base): + __tablename__ = "parent" + id = Column(Integer, primary_key=True) + if use_same_mappers: + child1 = relationship( + Child, + primaryjoin=lambda: and_( + Child.parent_id == Parent.id, Child.num == 1 + ), + overlaps="child2" if setup_overlaps else None, + ) + child2 = relationship( + Child, + primaryjoin=lambda: and_( + Child.parent_id == Parent.id, Child.num == 2 + ), + overlaps="child1" if setup_overlaps else None, + ) + else: + child1 = relationship( + c1, + primaryjoin=lambda: and_( + c1.parent_id == Parent.id, c1.num == 1 + ), + overlaps="child2" if setup_overlaps else None, + ) + + child2 = relationship( + c2, + primaryjoin=lambda: and_( + c2.parent_id == Parent.id, c2.num == 1 + ), + overlaps="child1" if setup_overlaps else None, + ) + + configure_mappers() + @testing.provide_metadata def _test_fixture_one_run(self, **kw): A, AMember, B, BSub1, BSub2 = self._fixture_one(**kw) @@ -748,6 +834,8 @@ class OverlappingFksSiblingTest(fixtures.TestBase): session.commit() assert bsub1.a is a2 # because bsub1.a_member is not a relationship + + assert BSub2.__mapper__.attrs.a.viewonly assert bsub2.a is a1 # because bsub2.a is viewonly=True # everyone has a B.a relationship @@ -757,6 +845,46 @@ class OverlappingFksSiblingTest(fixtures.TestBase): ) @testing.provide_metadata + def test_simple_warn(self): + assert_raises_message( + exc.SAWarning, + r"relationship '(?:Child.parent|Parent.children)' will copy " + r"column parent.id to column child.parent_id, which conflicts " + r"with relationship\(s\): '(?:Parent.children|Child.parent)' " + r"\(copies parent.id to child.parent_id\).", + self._fixture_two, + setup_backrefs=False, + ) + + @testing.provide_metadata + def test_simple_backrefs_works(self): + self._fixture_two(setup_backrefs=True) + + @testing.provide_metadata + def test_simple_overlaps_works(self): + self._fixture_two(setup_overlaps=True) + + @testing.provide_metadata + def test_double_rel_same_mapper_warns(self): + assert_raises_message( + exc.SAWarning, + r"relationship 'Parent.child[12]' will copy column parent.id to " + r"column child.parent_id, which conflicts with relationship\(s\): " + r"'Parent.child[12]' \(copies parent.id to child.parent_id\)", + self._fixture_three, + use_same_mappers=True, + setup_overlaps=False, + ) + + @testing.provide_metadata + def test_double_rel_same_mapper_overlaps_works(self): + self._fixture_three(use_same_mappers=True, setup_overlaps=True) + + @testing.provide_metadata + def test_double_rel_aliased_mapper_works(self): + self._fixture_three(use_same_mappers=False, setup_overlaps=False) + + @testing.provide_metadata def test_warn_one(self): assert_raises_message( exc.SAWarning, @@ -791,6 +919,17 @@ class OverlappingFksSiblingTest(fixtures.TestBase): ) @testing.provide_metadata + def test_warn_four(self): + assert_raises_message( + exc.SAWarning, + r"relationship '(?:B.a|BSub2.a_member|B.a)' will copy column " + r"(?:a.id|a_member.a_id) to column b.a_id", + self._fixture_one, + add_bsub2_a_viewonly=True, + add_b_a=True, + ) + + @testing.provide_metadata def test_works_one(self): self._test_fixture_one_run( add_b_a=True, add_b_a_viewonly=True, add_bsub1_a=True @@ -798,7 +937,10 @@ class OverlappingFksSiblingTest(fixtures.TestBase): @testing.provide_metadata def test_works_two(self): - self._test_fixture_one_run(add_b_a=True, add_bsub2_a_viewonly=True) + # doesn't actually work with real FKs beacuse it creates conflicts :) + self._fixture_one( + add_b_a=True, add_b_a_overlaps="a_member", add_bsub1_a=True + ) class CompositeSelfRefFKTest(fixtures.MappedTest, AssertsCompiledSQL): diff --git a/test/orm/test_selectin_relations.py b/test/orm/test_selectin_relations.py index 4eecc4be6..8453a2606 100644 --- a/test/orm/test_selectin_relations.py +++ b/test/orm/test_selectin_relations.py @@ -833,10 +833,16 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): Address, lazy="selectin", order_by=addresses.c.id ), open_orders=relationship( - open_mapper, lazy="selectin", order_by=open_mapper.id + open_mapper, + lazy="selectin", + order_by=open_mapper.id, + overlaps="closed_orders", ), closed_orders=relationship( - closed_mapper, lazy="selectin", order_by=closed_mapper.id + closed_mapper, + lazy="selectin", + order_by=closed_mapper.id, + overlaps="open_orders", ), ), ) @@ -900,6 +906,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="selectin", order_by=open_mapper.id, + viewonly=True, ), closed_orders=relationship( closed_mapper, @@ -909,6 +916,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="selectin", order_by=closed_mapper.id, + viewonly=True, ), ), ) @@ -969,6 +977,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="selectin", order_by=orders.c.id, + overlaps="closed_orders", ), closed_orders=relationship( Order, @@ -977,6 +986,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="selectin", order_by=orders.c.id, + overlaps="open_orders", ), ), ) @@ -3108,7 +3118,7 @@ class M2OWDegradeTest( id = Column(Integer, primary_key=True) b_id = Column(ForeignKey("b.id")) b = relationship("B") - b_no_omit_join = relationship("B", omit_join=False) + b_no_omit_join = relationship("B", omit_join=False, overlaps="b") q = Column(Integer) class B(fixtures.ComparableEntity, Base): diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py index 4c68d154e..8bc146f18 100644 --- a/test/orm/test_subquery_relations.py +++ b/test/orm/test_subquery_relations.py @@ -919,6 +919,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="subquery", order_by=open_mapper.id, + overlaps="closed_orders", ), closed_orders=relationship( closed_mapper, @@ -928,6 +929,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="subquery", order_by=closed_mapper.id, + overlaps="open_orders", ), ), ) @@ -988,6 +990,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="subquery", order_by=orders.c.id, + viewonly=True, ), closed_orders=relationship( Order, @@ -996,6 +999,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL): ), lazy="subquery", order_by=orders.c.id, + viewonly=True, ), ), ) diff --git a/test/orm/test_unitofwork.py b/test/orm/test_unitofwork.py index 58eb62339..235817db9 100644 --- a/test/orm/test_unitofwork.py +++ b/test/orm/test_unitofwork.py @@ -1786,6 +1786,7 @@ class OneToManyTest(_fixtures.FixtureTest): users.c.id == addresses.c.user_id, addresses.c.email_address.like("%boston%"), ), + overlaps="newyork_addresses", ), "newyork_addresses": relationship( m2, @@ -1793,6 +1794,7 @@ class OneToManyTest(_fixtures.FixtureTest): users.c.id == addresses.c.user_id, addresses.c.email_address.like("%newyork%"), ), + overlaps="boston_addresses", ), }, ) |