summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-12-31 11:46:30 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2010-12-31 11:46:30 -0500
commit9d04eaffcc600befce98117eabe82e0ead0dc741 (patch)
treef6a25ed870b9ce1771bddc5fe4c04a6d04f43cf4 /lib/sqlalchemy
parentd9b032e36ac5deace63c25ce6daebf302293d64c (diff)
downloadsqlalchemy-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.py4
-rw-r--r--lib/sqlalchemy/orm/deprecated_interfaces.py2
-rw-r--r--lib/sqlalchemy/orm/descriptor_props.py2
-rw-r--r--lib/sqlalchemy/orm/events.py49
-rw-r--r--lib/sqlalchemy/orm/mapper.py53
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())