diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-12-31 11:46:30 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-12-31 11:46:30 -0500 |
| commit | 9d04eaffcc600befce98117eabe82e0ead0dc741 (patch) | |
| tree | f6a25ed870b9ce1771bddc5fe4c04a6d04f43cf4 /lib/sqlalchemy | |
| parent | d9b032e36ac5deace63c25ce6daebf302293d64c (diff) | |
| download | sqlalchemy-9d04eaffcc600befce98117eabe82e0ead0dc741.tar.gz | |
- add QueryContext to load(), refresh()
- add list of attribute names to refresh()
- ensure refresh() only called when attributes actually refreshed
- tests. [ticket:2011]
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/ext/mutable.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/deprecated_interfaces.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/descriptor_props.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/events.py | 49 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 53 |
5 files changed, 73 insertions, 37 deletions
diff --git a/lib/sqlalchemy/ext/mutable.py b/lib/sqlalchemy/ext/mutable.py index f3bd91efb..e849dfcf3 100644 --- a/lib/sqlalchemy/ext/mutable.py +++ b/lib/sqlalchemy/ext/mutable.py @@ -51,7 +51,7 @@ class Mutable(object): key = attribute.key parent_cls = attribute.class_ - def load(state): + def load(state, *args): """Listen for objects loaded or refreshed. Wrap the target data member's value with @@ -230,7 +230,7 @@ class MutableComposite(object): key = attribute.key parent_cls = attribute.class_ - def load(state): + def load(state, *args): """Listen for objects loaded or refreshed. Wrap the target data member's value with diff --git a/lib/sqlalchemy/orm/deprecated_interfaces.py b/lib/sqlalchemy/orm/deprecated_interfaces.py index b294a8d7d..26f164509 100644 --- a/lib/sqlalchemy/orm/deprecated_interfaces.py +++ b/lib/sqlalchemy/orm/deprecated_interfaces.py @@ -83,7 +83,7 @@ class MapperExtension(object): if me_meth is not ls_meth: if meth == 'reconstruct_instance': def go(ls_meth): - def reconstruct(instance): + def reconstruct(instance, ctx): ls_meth(self, instance) return reconstruct event.listen(self.class_manager, 'load', diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py index 2c3a7559d..cda71f607 100644 --- a/lib/sqlalchemy/orm/descriptor_props.py +++ b/lib/sqlalchemy/orm/descriptor_props.py @@ -155,7 +155,7 @@ class CompositeProperty(DescriptorProperty): def _setup_event_handlers(self): """Establish events that populate/expire the composite attribute.""" - def load_handler(state): + def load_handler(state, *args): dict_ = state.dict if self.key in dict_: diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py index bb011e5f7..b45f3ba6b 100644 --- a/lib/sqlalchemy/orm/events.py +++ b/lib/sqlalchemy/orm/events.py @@ -121,7 +121,7 @@ class InstanceEvents(event.Events): """ - def load(self, target): + def load(self, target, context): """Receive an object instance after it has been created via ``__new__``, and after initial attribute population has occurred. @@ -135,29 +135,59 @@ class InstanceEvents(event.Events): attributes and collections may or may not be loaded or even initialized, depending on what's present in the result rows. + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param context: the :class:`.QueryContext` corresponding to the + current :class:`.Query` in progress. + """ - def refresh(self, target): + def refresh(self, target, context, attrs): """Receive an object instance after one or more attributes have - been refreshed. + been refreshed from a query. - This hook is called after expired attributes have been reloaded. + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param context: the :class:`.QueryContext` corresponding to the + current :class:`.Query` in progress. + :param attrs: iterable collection of attribute names which + were populated, or None if all column-mapped, non-deferred + attributes were populated. """ - def expire(self, target, keys): + def expire(self, target, attrs): """Receive an object instance after its attributes or some subset have been expired. 'keys' is a list of attribute names. If None, the entire state was expired. - + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + :param attrs: iterable collection of attribute + names which were expired, or None if all attributes were + expired. + """ def resurrect(self, target): """Receive an object instance as it is 'resurrected' from garbage collection, which occurs when a "dirty" state falls - out of scope.""" + out of scope. + + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. + + """ class MapperEvents(event.Events): @@ -412,7 +442,10 @@ class MapperEvents(event.Events): :param row: the result row being handled. This may be an actual :class:`.RowProxy` or may be a dictionary containing :class:`.Column` objects as keys. - :param class\_: the mapped class. + :param target: the mapped instance. If + the event is configured with ``raw=True``, this will + instead be the :class:`.InstanceState` state-management + object associated with the instance. :return: When configured with ``retval=True``, a return value of ``EXT_STOP`` will bypass instance population by the mapper. A value of ``EXT_CONTINUE`` indicates that diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index a0265f9a8..5ee9d58c3 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2269,37 +2269,40 @@ class Mapper(object): else: populate_state(state, dict_, row, isnew, only_load_props) - else: + if loaded_instance: + state.manager.dispatch.load(state, context) + elif isnew: + state.manager.dispatch.refresh(state, context, only_load_props) + + elif state in context.partials or state.unloaded: # populate attributes on non-loading instances which have # been expired # TODO: apply eager loads to un-lazy loaded collections ? - if state in context.partials or state.unloaded: - if state in context.partials: - isnew = False - (d_, attrs) = context.partials[state] - else: - isnew = True - attrs = state.unloaded - # allow query.instances to commit the subset of attrs - context.partials[state] = (dict_, attrs) - - if populate_instance: - for fn in populate_instance: - ret = fn(self, context, row, state, - only_load_props=attrs, - instancekey=identitykey, isnew=isnew) - if ret is not EXT_CONTINUE: - break - else: - populate_state(state, dict_, row, isnew, attrs) + if state in context.partials: + isnew = False + (d_, attrs) = context.partials[state] + else: + isnew = True + attrs = state.unloaded + # allow query.instances to commit the subset of attrs + context.partials[state] = (dict_, attrs) + + if populate_instance: + for fn in populate_instance: + ret = fn(self, context, row, state, + only_load_props=attrs, + instancekey=identitykey, isnew=isnew) + if ret is not EXT_CONTINUE: + break else: populate_state(state, dict_, row, isnew, attrs) + else: + populate_state(state, dict_, row, isnew, attrs) + + if isnew: + state.manager.dispatch.refresh(state, context, attrs) - if loaded_instance: - state.manager.dispatch.load(state) - elif isnew: - state.manager.dispatch.refresh(state) if result is not None: if append_result: @@ -2462,7 +2465,7 @@ def validates(*names): return fn return wrap -def _event_on_load(state): +def _event_on_load(state, ctx): instrumenting_mapper = state.manager.info[_INSTRUMENTOR] if instrumenting_mapper._reconstructor: instrumenting_mapper._reconstructor(state.obj()) |
