Merge "Don't expire "deferred" attributes in make_transient_to_detached"

This commit is contained in:
mike bayer
2017-09-27 15:47:10 -04:00
committed by Gerrit Code Review
4 changed files with 63 additions and 1 deletions
+8
View File
@@ -0,0 +1,8 @@
.. change::
:tags: bug, orm
:tickets: 4084
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.
+1 -1
View File
@@ -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):
+13
View File
@@ -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(
+41
View File
@@ -13,6 +13,7 @@ from sqlalchemy.orm import mapper, relationship, create_session, \
from sqlalchemy.testing import fixtures
from test.orm import _fixtures
from sqlalchemy.sql import select
from sqlalchemy.orm import make_transient_to_detached
class ExpireTest(_fixtures.FixtureTest):
@@ -1035,6 +1036,46 @@ class ExpireTest(_fixtures.FixtureTest):
.expired_attributes
assert 'addresses' not in attributes.instance_state(u1).callables
def test_deferred_expire_w_transient_to_detached(self):
orders, Order = self.tables.orders, self.classes.Order
mapper(Order, orders, properties={
"description": deferred(orders.c.description)
})
s = Session()
item = Order(id=1)
make_transient_to_detached(item)
s.add(item)
item.isopen
assert 'description' not in item.__dict__
def test_deferred_expire_normally(self):
orders, Order = self.tables.orders, self.classes.Order
mapper(Order, orders, properties={
"description": deferred(orders.c.description)
})
s = Session()
item = s.query(Order).first()
s.expire(item)
item.isopen
assert 'description' not in item.__dict__
def test_deferred_expire_explicit_attrs(self):
orders, Order = self.tables.orders, self.classes.Order
mapper(Order, orders, properties={
"description": deferred(orders.c.description)
})
s = Session()
item = s.query(Order).first()
s.expire(item, ['isopen', 'description'])
item.isopen
assert 'description' in item.__dict__
class PolymorphicExpireTest(fixtures.MappedTest):
run_inserts = 'once'