summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/loading.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-08-29 16:28:19 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-08-29 16:28:19 -0400
commit5686472f42468afc98d7daf61b850333aebb6a9d (patch)
tree65a002ffedbd241927bab476b02a6cb8e143645c /lib/sqlalchemy/orm/loading.py
parent9449c102768e9dc14d28405caec31a9957d52408 (diff)
downloadsqlalchemy-5686472f42468afc98d7daf61b850333aebb6a9d.tar.gz
- defaultdict benchmarks faster than a namedtuple; OK
- inline the column-based expiration operations as well
Diffstat (limited to 'lib/sqlalchemy/orm/loading.py')
-rw-r--r--lib/sqlalchemy/orm/loading.py148
1 files changed, 85 insertions, 63 deletions
diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py
index bcb783480..d3d170d94 100644
--- a/lib/sqlalchemy/orm/loading.py
+++ b/lib/sqlalchemy/orm/loading.py
@@ -213,9 +213,6 @@ def load_on_ident(query, key,
except orm_exc.NoResultFound:
return None
-_populator_struct = collections.namedtuple(
- 'populators', ['new', 'existing', 'eager', 'delayed'])
-
def instance_processor(mapper, context, result, path, adapter,
polymorphic_from=None,
@@ -258,7 +255,7 @@ def instance_processor(mapper, context, result, path, adapter,
identity_class = mapper._identity_class
- populators = _populator_struct([], [], [], [])
+ populators = collections.defaultdict(list)
props = mapper._props.values()
if only_load_props is not None:
@@ -268,16 +265,14 @@ def instance_processor(mapper, context, result, path, adapter,
prop.create_row_processor(
context, path, mapper, result, adapter, populators)
- if populators.delayed:
- populators.new.extend(populators.delayed)
-
- (new_populators, existing_populators,
- eager_populators) = (
- populators.new, populators.existing, populators.eager)
+ quick_populators = populators.get('quick', ())
+ expire_populators = populators.get('expire', ())
+ new_populators = populators.get('new', []) + populators.get('delayed', [])
+ existing_populators = populators.get('existing', ())
+ eager_populators = populators.get('eager', ())
load_path = context.query._current_path + path \
- if context.query._current_path.path \
- else path
+ if context.query._current_path.path else path
session_identity_map = context.session.identity_map
@@ -380,69 +375,96 @@ def instance_processor(mapper, context, result, path, adapter,
session_identity_map._add_unpresent(state, identitykey)
if currentload or populate_existing:
- # state is being fully loaded, so populate.
- # add to the "context.progress" collection.
+ # full population routines. Objects here are either
+ # just created, or we are doing a populate_existing
if isnew:
+ # first time we are seeing a row with this identity.
state.runid = runid
if propagate_options:
state.load_options = propagate_options
if state.load_options:
state.load_path = load_path
+
+ for key, getter in quick_populators:
+ dict_[key] = getter(row)
+ if populate_existing:
+ for key, set_callable in expire_populators:
+ dict_.pop(key, None)
+ if set_callable:
+ state.callables[key] = state
+ else:
+ for key, set_callable in expire_populators:
+ if set_callable:
+ state.callables[key] = state
for key, populator in new_populators:
populator(state, dict_, row)
+
+ if loaded_instance and load_evt:
+ state.manager.dispatch.load(state, context)
+ elif isnew and refresh_evt:
+ state.manager.dispatch.refresh(
+ state, context, only_load_props)
+
+ if populate_existing or state.modified:
+ if refresh_state and only_load_props:
+ state._commit(dict_, only_load_props)
+ else:
+ state._commit_all(dict_, session_identity_map)
else:
+ # have already seen rows with this identity.
for key, populator in existing_populators:
populator(state, dict_, row)
-
- if loaded_instance and load_evt:
- state.manager.dispatch.load(state, context)
- elif isnew and refresh_evt:
- state.manager.dispatch.refresh(
- state, context, only_load_props)
-
- if populate_existing or state.modified:
- if refresh_state and only_load_props:
- state._commit(dict_, only_load_props)
- else:
- state._commit_all(dict_, session_identity_map)
-
- elif state in context.partials or state.unloaded or eager_populators:
- # state is having a partial set of its attributes
- # refreshed. Populate those attributes,
- # and add to the "context.partials" collection.
+ else:
+ # partial population routines, for objects that were already
+ # in the Session, but a row matches them; apply eager loaders
+ # on existing objects, etc.
unloaded = state.unloaded
-
- if state in context.partials:
- isnew = False
- to_load = context.partials[state]
- for key, populator in existing_populators:
- if key not in to_load:
- continue
- populator(state, dict_, row)
- else:
- isnew = True
- to_load = unloaded
- context.partials[state] = to_load
-
- if context.propagate_options:
- state.load_options = context.propagate_options
- if state.load_options:
- state.load_path = load_path
-
- for key, populator in new_populators:
- if key not in to_load:
- continue
- populator(state, dict_, row)
-
- for key, pop in eager_populators:
- if key not in unloaded:
- pop(state, dict_, row)
-
- if isnew and refresh_evt:
- state.manager.dispatch.refresh(state, context, to_load)
-
- if isnew:
- state._commit(dict_, to_load)
+ isnew = state not in context.partials
+
+ if not isnew or unloaded or eager_populators:
+ # state is having a partial set of its attributes
+ # refreshed. Populate those attributes,
+ # and add to the "context.partials" collection.
+
+ if not isnew:
+ to_load = context.partials[state]
+ for key, populator in existing_populators:
+ if key not in to_load:
+ continue
+ populator(state, dict_, row)
+ else:
+ to_load = unloaded
+ context.partials[state] = to_load
+
+ if context.propagate_options:
+ state.load_options = context.propagate_options
+ if state.load_options:
+ state.load_path = load_path
+
+ for key, getter in quick_populators:
+ if key not in to_load:
+ continue
+ dict_[key] = getter(row)
+ for key, set_callable in expire_populators:
+ if key not in to_load:
+ continue
+ dict_.pop(key, None)
+ if set_callable:
+ state.callables[key] = state
+ for key, populator in new_populators:
+ if key not in to_load:
+ continue
+ populator(state, dict_, row)
+
+ for key, pop in eager_populators:
+ if key not in unloaded:
+ pop(state, dict_, row)
+
+ if isnew:
+ if refresh_evt:
+ state.manager.dispatch.refresh(state, context, to_load)
+
+ state._commit(dict_, to_load)
return instance
return _instance