mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-28 03:26:01 -04:00
Merge "Don't expire "deferred" attributes in make_transient_to_detached"
This commit is contained in:
+8
@@ -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.
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user