summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/mapper.py4
-rw-r--r--lib/sqlalchemy/orm/properties.py4
-rw-r--r--lib/sqlalchemy/orm/query.py5
-rw-r--r--lib/sqlalchemy/orm/session.py13
-rw-r--r--lib/sqlalchemy/orm/state.py31
-rw-r--r--lib/sqlalchemy/orm/strategies.py4
6 files changed, 41 insertions, 20 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 5a6e24dfa..a8c525657 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -1484,7 +1484,7 @@ class Mapper(object):
)
if readonly:
- _expire_state(state, readonly)
+ _expire_state(state, state.dict, readonly)
# if specified, eagerly refresh whatever has
# been expired.
@@ -1524,7 +1524,7 @@ class Mapper(object):
deferred_props = [prop.key for prop in [self._columntoproperty[c] for c in postfetch_cols]]
if deferred_props:
- _expire_state(state, deferred_props)
+ _expire_state(state, state.dict, deferred_props)
# synchronize newly inserted ids from one table to the next
# TODO: this still goes a little too often. would be nice to
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 8dbc6b3db..bb92f39e4 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -115,7 +115,7 @@ class ColumnProperty(StrategizedProperty):
impl = dest_state.get_impl(self.key)
impl.set(dest_state, dest_dict, value, None)
else:
- dest_state.expire_attributes([self.key])
+ dest_state.expire_attributes(dest_dict, [self.key])
def get_col_value(self, column, value):
return value
@@ -636,7 +636,7 @@ class RelationProperty(StrategizedProperty):
return
if not "merge" in self.cascade:
- dest_state.expire_attributes([self.key])
+ dest_state.expire_attribute(dest_dict, [self.key])
return
if self.key not in source_dict:
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index ed5535151..456f1f19c 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -1873,8 +1873,9 @@ class Query(object):
state.commit(dict_, list(to_evaluate))
- # expire attributes with pending changes (there was no autoflush, so they are overwritten)
- state.expire_attributes(set(evaluated_keys).difference(to_evaluate))
+ # expire attributes with pending changes
+ # (there was no autoflush, so they are overwritten)
+ state.expire_attributes(dict_, set(evaluated_keys).difference(to_evaluate))
elif synchronize_session == 'fetch':
target_mapper = self._mapper_zero()
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 0e5e939b1..d5246bee0 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -289,14 +289,14 @@ class SessionTransaction(object):
assert not self.session._deleted
for s in self.session.identity_map.all_states():
- _expire_state(s, None, instance_dict=self.session.identity_map)
+ _expire_state(s, s.dict, None, instance_dict=self.session.identity_map)
def _remove_snapshot(self):
assert self._is_transaction_boundary
if not self.nested and self.session.expire_on_commit:
for s in self.session.identity_map.all_states():
- _expire_state(s, None, instance_dict=self.session.identity_map)
+ _expire_state(s, s.dict, None, instance_dict=self.session.identity_map)
def _connection_for_bind(self, bind):
self._assert_is_active()
@@ -915,7 +915,7 @@ class Session(object):
"""Expires all persistent instances within this Session."""
for state in self.identity_map.all_states():
- _expire_state(state, None, instance_dict=self.identity_map)
+ _expire_state(state, state.dict, None, instance_dict=self.identity_map)
def expire(self, instance, attribute_names=None):
"""Expire the attributes on an instance.
@@ -936,14 +936,15 @@ class Session(object):
raise exc.UnmappedInstanceError(instance)
self._validate_persistent(state)
if attribute_names:
- _expire_state(state, attribute_names=attribute_names, instance_dict=self.identity_map)
+ _expire_state(state, state.dict,
+ attribute_names=attribute_names, instance_dict=self.identity_map)
else:
# pre-fetch the full cascade since the expire is going to
# remove associations
cascaded = list(_cascade_state_iterator('refresh-expire', state))
- _expire_state(state, None, instance_dict=self.identity_map)
+ _expire_state(state, state.dict, None, instance_dict=self.identity_map)
for (state, m, o) in cascaded:
- _expire_state(state, None, instance_dict=self.identity_map)
+ _expire_state(state, state.dict, None, instance_dict=self.identity_map)
def prune(self):
"""Remove unreferenced instances cached in the identity map.
diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py
index 4bb9219f4..a9494a50e 100644
--- a/lib/sqlalchemy/orm/state.py
+++ b/lib/sqlalchemy/orm/state.py
@@ -239,9 +239,30 @@ class InstanceState(object):
return set(
key for key in self.manager.iterkeys()
if key not in self.committed_state and key not in self.dict)
-
- def expire_attributes(self, attribute_names, instance_dict=None):
- self.expired_attributes = set(self.expired_attributes)
+
+ def expire_attribute_pre_commit(self, dict_, key):
+ """a fast expire that can be called by column loaders during a load.
+
+ The additional bookkeeping is finished up in commit_all().
+
+ This method is actually called a lot with joined-table
+ loading, when the second table isn't present in the result.
+
+ """
+ # TODO: yes, this is still a little too busy.
+ # need to more cleanly separate out handling
+ # for the various AttributeImpls and the contracts
+ # they wish to maintain with their strategies
+ if not self.expired_attributes:
+ self.expired_attributes = set(self.expired_attributes)
+
+ dict_.pop(key, None)
+ self.callables[key] = self
+ self.expired_attributes.add(key)
+
+ def expire_attributes(self, dict_, attribute_names, instance_dict=None):
+ if not self.expired_attributes:
+ self.expired_attributes = set(self.expired_attributes)
if attribute_names is None:
attribute_names = self.manager.keys()
@@ -258,7 +279,6 @@ class InstanceState(object):
filter_deferred = True
else:
filter_deferred = False
- dict_ = self.dict
for key in attribute_names:
impl = self.manager[key].impl
@@ -354,8 +374,7 @@ class InstanceState(object):
self.committed_state = {}
self.pending = {}
-
- # unexpire attributes which have loaded
+
if self.expired_attributes:
for key in self.expired_attributes.intersection(dict_):
self.callables.pop(key, None)
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 5e81d33ca..4d5ec3da4 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -121,7 +121,7 @@ class ColumnLoader(LoaderStrategy):
else:
def new_execute(state, dict_, row, isnew):
if isnew:
- state.expire_attributes([key])
+ state.expire_attribute_pre_commit(dict_, key)
return new_execute, None
log.class_logger(ColumnLoader)
@@ -168,7 +168,7 @@ class CompositeColumnLoader(ColumnLoader):
if c not in row:
def new_execute(state, dict_, row, isnew):
if isnew:
- state.expire_attributes([key])
+ state.expire_attribute_pre_commit(dict_, key)
break
else:
def new_execute(state, dict_, row, isnew):