--- /dev/null
+.. change::
+ :tags: bug, regression, orm
+ :tickets: 6426
+
+ Fixed regression involving ``lazy='dynamic'`` loader in conjunction with a
+ detached object. The previous behavior was that the dynamic loader upon
+ calling methods like ``.all()`` returns empty lists for detached objects
+ without error, this has been restored; however a warning is now emitted as
+ this is not the correct result. Other dynamic loader scenarios correctly
+ raise ``DetachedInstanceError``.
"""
- def __init__(self, cursor_metadata, iterator, raw=None):
+ def __init__(
+ self,
+ cursor_metadata,
+ iterator,
+ raw=None,
+ _source_supports_scalars=False,
+ ):
self._metadata = cursor_metadata
self.iterator = iterator
self.raw = raw
+ self._source_supports_scalars = _source_supports_scalars
def _soft_close(self, **kw):
self.iterator = iter([])
from .. import exc
from .. import log
from .. import util
+from ..engine import result
@log.class_logger
session = property(session, lambda s, x: None)
- def __iter__(self):
+ def _iter(self):
sess = self.session
if sess is None:
- return iter(
+ state = attributes.instance_state(self.instance)
+ if state.detached:
+ util.warn(
+ "Instance %s is detached, dynamic relationship cannot "
+ "return a correct result. This warning will become "
+ "a DetachedInstanceError in a future release."
+ % (orm_util.state_str(state))
+ )
+
+ return result.IteratorResult(
+ result.SimpleResultMetaData([self.attr.class_.__name__]),
self.attr._get_collection_history(
attributes.instance_state(self.instance),
attributes.PASSIVE_NO_INITIALIZE,
- ).added_items
- )
+ ).added_items,
+ _source_supports_scalars=True,
+ ).scalars()
else:
- return iter(self._generate(sess))
+ return self._generate(sess)._iter()
def __getitem__(self, index):
sess = self.session
)
def test_detached_raise(self):
+ """so filtering on a detached dynamic list raises an error..."""
+
User, Address = self._user_address_fixture()
sess = fixture_session()
u = sess.query(User).get(8)
email_address="e",
)
+ def test_detached_all_empty_list(self):
+ """test #6426 - but you can call .all() on it and you get an empty
+ list. This is legacy stuff, as this should be raising
+ DetachedInstanceError.
+
+ """
+
+ User, Address = self._user_address_fixture()
+ sess = fixture_session()
+ u = sess.query(User).get(8)
+ sess.expunge(u)
+
+ with testing.expect_warnings(
+ r"Instance <User .*> is detached, dynamic relationship"
+ ):
+ eq_(u.addresses.all(), [])
+
+ with testing.expect_warnings(
+ r"Instance <User .*> is detached, dynamic relationship"
+ ):
+ eq_(list(u.addresses), [])
+
+ def test_transient_all_empty_list(self):
+ User, Address = self._user_address_fixture()
+ u1 = User()
+ eq_(u1.addresses.all(), [])
+
+ eq_(list(u1.addresses), [])
+
def test_no_uselist_false(self):
User, Address = self._user_address_fixture(
addresses_args={"uselist": False}