--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 5149
+
+ Setting a relationship to viewonly=True which is also the target of a
+ back_populates or backref configuration will now emit a warning and
+ eventually be disallowed. back_populates refers specifically to mutation
+ of an attribute or collection, which is disallowed when the attribute is
+ subject to viewonly=True. The viewonly attribute is not subject to
+ persistence behaviors which means it will not reflect correct results
+ when it is locally mutated.
the full set of related objects, to prevent modifications of the
collection from resulting in persistence operations.
+ .. warning:: The viewonly=True relationship should not be mutated
+ in Python; that means, elements should not be added or removed
+ from collections nor should a many-to-one or one-to-one attribute
+ be altered in Python. The viewonly=True relationship should only
+ be accessed via read. Towards this behavior, it is also not
+ appropriate for the viewonly=True relationship to have any kind
+ of persistence cascade settings, nor should it be the target of
+ either :paramref:`.relationship.backref` or
+ :paramref:`.relationship.back_populates`, as backrefs imply
+ in-Python mutation of the attribute. SQLAlchemy may emit
+ warnings for some or all of these conditions as of the 1.3 and
+ 1.4 series of SQLAlchemy and will eventually be disallowed.
+
:param omit_join:
Allows manual control over the "selectin" automatic join
optimization. Set to ``False`` to disable the "omit join" feature
def _add_reverse_property(self, key):
other = self.mapper.get_property(key, _configure_mappers=False)
+ if other.viewonly:
+ util.warn_limited(
+ "Setting backref / back_populates on relationship %s to refer "
+ "to viewonly relationship %s will be deprecated in SQLAlchemy "
+ "1.4, and will be disallowed in a future release. "
+ "viewonly relationships should not be mutated",
+ (self, other),
+ )
self._reverse_property.add(other)
other._reverse_property.add(self)
)
mapper(B, self.tables.t2)
+ with testing.expect_warnings(
+ "Setting backref / back_populates on "
+ "relationship B.a to refer to viewonly relationship A.bs"
+ ):
+ configure_mappers()
+
a1 = A()
b1 = B()
a1.bs.append(b1)
)
mapper(B, self.tables.t2)
+ with testing.expect_warnings(
+ "Setting backref / back_populates on "
+ "relationship A.bs to refer to viewonly relationship B.a"
+ ):
+ configure_mappers()
+
a1 = A()
b1 = B()
b1.a = a1
)
mapper(B, t2)
+ with testing.expect_warnings(
+ "Setting backref / back_populates on "
+ "relationship A.bs to refer to viewonly relationship B.a"
+ ):
+ configure_mappers()
+
sess = create_session()
a1 = A()
b1 = B(as_=[a1])