relationship X will copy column Q to column P, which conflicts with relationship(s): 'Y'
----------------------------------------------------------------------------------------
-This warning refers to the case when two or more relationships will write data to the
-same columns on flush, but the ORM does not have any kind of back population configuration
-between the two relationships. The fix is usually to install the correct
-:paramref:`_orm.relationship.back_populates` configuration. Given the following mapping::
+This warning refers to the case when two or more relationships will write data
+to the same columns on flush, but the ORM does not have any means of
+coordinating these relationships together. Depending on specifics, the solution
+may be that two relationships need to be referred towards one another using
+:paramref:`_orm.relationship.back_populates`, or that one or more of the
+relationships should be configured with :paramref:`_orm.relationship.viewonly`
+to prevent conflicting writes, or sometimes that the configuration is fully
+intentional and should configure :paramref:`_orm.relationship.overlaps` to
+silence each warning.
+
+For the typical example that's missing
+:paramref:`_orm.relationship.back_populates`, given the following mapping::
class Parent(Base):
__tablename__ = "parent"
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 column_property
from sqlalchemy.orm import composite
from sqlalchemy.orm import configure_mappers
+from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import exc as orm_exc
from sqlalchemy.orm import foreign
from sqlalchemy.orm import joinedload
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import in_
from sqlalchemy.testing import is_
+from sqlalchemy.testing.assertions import expect_warnings
from sqlalchemy.testing.assertsql import assert_engine
from sqlalchemy.testing.assertsql import CompiledSQL
from sqlalchemy.testing.fixtures import fixture_session
setup_backrefs=False,
)
+ @testing.combinations((True,), (False,), argnames="set_overlaps")
+ def test_fixture_five(self, metadata, set_overlaps):
+ Base = declarative_base(metadata=self.metadata)
+
+ if set_overlaps:
+ overlaps = "as,cs"
+ else:
+ overlaps = None
+
+ class A(Base):
+ __tablename__ = "a"
+
+ id = Column(Integer, primary_key=True)
+ cs = relationship("C", secondary="b", backref="as")
+ bs = relationship("B", back_populates="a", overlaps=overlaps)
+
+ class B(Base):
+ __tablename__ = "b"
+
+ a_id = Column(ForeignKey("a.id"), primary_key=True)
+ c_id = Column(ForeignKey("c.id"), primary_key=True)
+ a = relationship("A", back_populates="bs", overlaps=overlaps)
+ c = relationship("C", back_populates="bs", overlaps=overlaps)
+
+ class C(Base):
+ __tablename__ = "c"
+
+ id = Column(Integer, primary_key=True)
+ bs = relationship("B", back_populates="c", overlaps=overlaps)
+
+ if set_overlaps:
+ configure_mappers()
+ else:
+ with expect_warnings(
+ r"relationship 'A.bs' will copy column a.id to column b.a_id, "
+ r"which conflicts with relationship\(s\): "
+ r"'C.as' \(copies a.id to b.a_id\), "
+ r"'A.cs' \(copies a.id to b.a_id\)"
+ r".*add the parameter 'overlaps=\"as,cs\"' to the 'A.bs' "
+ r"relationship",
+ #
+ #
+ r"relationship 'B.a' will copy column a.id to column b.a_id, "
+ r"which conflicts with relationship\(s\): "
+ r"'C.as' \(copies a.id to b.a_id\), "
+ r"'A.cs' \(copies a.id to b.a_id\)..*"
+ r"add the parameter 'overlaps=\"as,cs\"' to the 'B.a' "
+ r"relationship",
+ #
+ #
+ r"relationship 'B.c' will copy column c.id to column b.c_id, "
+ r"which conflicts with relationship\(s\): "
+ r"'C.as' \(copies c.id to b.c_id\), "
+ r"'A.cs' \(copies c.id to b.c_id\)"
+ r".*add the parameter 'overlaps=\"as,cs\"' to the 'B.c' "
+ r"relationship",
+ #
+ #
+ r"relationship 'C.bs' will copy column c.id to column b.c_id, "
+ r"which conflicts with relationship\(s\): "
+ r"'C.as' \(copies c.id to b.c_id\), "
+ r"'A.cs' \(copies c.id to b.c_id\)"
+ r".*add the parameter 'overlaps=\"as,cs\"' to the 'C.bs' "
+ r"relationship",
+ ):
+ configure_mappers()
+
@testing.provide_metadata
def test_fixture_four(self):
self._fixture_four()