diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2022-09-25 23:15:46 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2022-09-25 23:15:46 +0000 |
| commit | 6072bebd19944be9ee1fdfd7fc4350d3317c9203 (patch) | |
| tree | b1812fc64a07b1a2ebd7256252a2c82cb6e7d358 | |
| parent | 65aa9b8a98c068de2f30ebfc719e4541df6ddcac (diff) | |
| parent | 29838ef584d49e5ecca08f76e4966454dc7f060f (diff) | |
| download | sqlalchemy-6072bebd19944be9ee1fdfd7fc4350d3317c9203.tar.gz | |
Merge "warn for local-only column in remote side" into main
| -rw-r--r-- | doc/build/changelog/unreleased_14/7094.rst | 9 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/relationships.py | 16 | ||||
| -rw-r--r-- | test/orm/test_cycles.py | 3 | ||||
| -rw-r--r-- | test/orm/test_relationships.py | 49 |
4 files changed, 74 insertions, 3 deletions
diff --git a/doc/build/changelog/unreleased_14/7094.rst b/doc/build/changelog/unreleased_14/7094.rst new file mode 100644 index 000000000..b6fb30d99 --- /dev/null +++ b/doc/build/changelog/unreleased_14/7094.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, orm + :tickets: 7094 + + A warning is emitted in ORM configurations when an explicit + :func:`_orm.remote` annotation is applied to columns that are local to the + immediate mapped class, when the referenced class does not include any of + the same table columns. Ideally this would raise an error at some point as + it's not correct from a mapping point of view. diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index 45b9b9bea..48d60647c 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -2806,6 +2806,22 @@ class JoinCondition: "condition that are on the remote side of " "the relationship." % (self.prop,) ) + else: + + not_target = util.column_set( + self.parent_persist_selectable.c + ).difference(self.child_persist_selectable.c) + + for _, rmt in self.local_remote_pairs: + if rmt in not_target: + util.warn( + "Expression %s is marked as 'remote', but these " + "column(s) are local to the local side. The " + "remote() annotation is needed only for a " + "self-referential relationship where both sides " + "of the relationship refer to the same tables." + % (rmt,) + ) def _check_foreign_cols( self, join_condition: ColumnElement[bool], primary: bool diff --git a/test/orm/test_cycles.py b/test/orm/test_cycles.py index 15155293f..a23d8b735 100644 --- a/test/orm/test_cycles.py +++ b/test/orm/test_cycles.py @@ -918,7 +918,6 @@ class OneToManyManyToOneTest(fixtures.MappedTest): favorite=relationship( Ball, primaryjoin=person.c.favorite_ball_id == ball.c.id, - remote_side=person.c.favorite_ball_id, post_update=True, _legacy_inactive_history_style=( self._legacy_inactive_history_style @@ -1036,7 +1035,6 @@ class OneToManyManyToOneTest(fixtures.MappedTest): favorite=relationship( Ball, primaryjoin=person.c.favorite_ball_id == ball.c.id, - remote_side=person.c.favorite_ball_id, _legacy_inactive_history_style=( self._legacy_inactive_history_style ), @@ -1096,7 +1094,6 @@ class OneToManyManyToOneTest(fixtures.MappedTest): favorite=relationship( Ball, primaryjoin=person.c.favorite_ball_id == ball.c.id, - remote_side=person.c.favorite_ball_id, _legacy_inactive_history_style=( self._legacy_inactive_history_style ), diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py index fd4230e15..8d27742c3 100644 --- a/test/orm/test_relationships.py +++ b/test/orm/test_relationships.py @@ -2589,6 +2589,55 @@ class JoinConditionErrorTest(fixtures.TestBase): registry.map_imperatively(C2, t3) assert C1.c2.property.primaryjoin.compare(t1.c.id == t3.c.t1id) + @testing.combinations( + "annotation", "local_remote", argnames="remote_anno_type" + ) + @testing.combinations("orm_col", "core_col", argnames="use_col_from") + def test_no_remote_on_local_only_cols( + self, decl_base, remote_anno_type, use_col_from + ): + """test #7094. + + a warning should be emitted for an inappropriate remote_side argument + + """ + + class A(decl_base): + __tablename__ = "a" + + id = Column(Integer, primary_key=True) + data = Column(String) + + if remote_anno_type == "annotation": + if use_col_from == "core_col": + bs = relationship( + "B", + primaryjoin=lambda: remote(A.__table__.c.id) + == B.__table__.c.a_id, + ) + elif use_col_from == "orm_col": + bs = relationship( + "B", primaryjoin="remote(A.id) == B.a_id" + ) + elif remote_anno_type == "local_remote": + if use_col_from == "core_col": + bs = relationship( + "B", remote_side=lambda: A.__table__.c.id + ) + elif use_col_from == "orm_col": + bs = relationship("B", remote_side="A.id") + + class B(decl_base): + __tablename__ = "b" + id = Column(Integer, primary_key=True) + a_id = Column(ForeignKey("a.id")) + + with expect_warnings( + r"Expression a.id is marked as 'remote', but these column\(s\) " + r"are local to the local side. " + ): + decl_base.registry.configure() + def test_join_error_raised(self, registry): m = MetaData() t1 = Table("t1", m, Column("id", Integer, primary_key=True)) |
