From 5abb036e9b9eba0f61cf9617dea2d879c2d5b09c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 26 Sep 2017 15:33:04 -0400 Subject: Don't expire "deferred" attributes in make_transient_to_detached Fixed issue where the :func:`.make_transient_to_detached` function would expire all attributes on the target object, including "deferred" attributes, which has the effect of the attribute being undeferred for the next refesh, causing an unexpected load of the attribute. Change-Id: I82a385e3033e3f3c31569b1e908efb5f258d0f27 Fixes: #4084 --- lib/sqlalchemy/orm/session.py | 2 +- lib/sqlalchemy/orm/state.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'lib/sqlalchemy') diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 359370ab5..0287f1cfb 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -3037,7 +3037,7 @@ def make_transient_to_detached(instance): if state._deleted: del state._deleted state._commit_all(state.dict) - state._expire_attributes(state.dict, state.unloaded) + state._expire_attributes(state.dict, state.unloaded_expirable) def object_session(instance): diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 2e53fe9e3..4964c22e6 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -610,6 +610,7 @@ class InstanceState(interfaces.InspectionAttr): def unmodified_intersection(self, keys): """Return self.unmodified.intersection(keys).""" + return set(keys).intersection(self.manager).\ difference(self.committed_state) @@ -625,6 +626,18 @@ class InstanceState(interfaces.InspectionAttr): difference(self.committed_state).\ difference(self.dict) + @property + def unloaded_expirable(self): + """Return the set of keys which do not have a loaded value. + + This includes expired attributes and any other attribute that + was never populated or modified. + + """ + return self.unloaded.intersection( + attr for attr in self.manager + if self.manager[attr].impl.expire_missing) + @property def _unloaded_non_object(self): return self.unloaded.intersection( -- cgit v1.2.1