summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/events.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/orm/events.py')
-rw-r--r--lib/sqlalchemy/orm/events.py246
1 files changed, 123 insertions, 123 deletions
diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py
index 5fe795db2..761ba315d 100644
--- a/lib/sqlalchemy/orm/events.py
+++ b/lib/sqlalchemy/orm/events.py
@@ -12,20 +12,20 @@ import inspect
class InstrumentationEvents(event.Events):
"""Events related to class instrumentation events.
-
+
The listeners here support being established against
any new style class, that is any object that is a subclass
of 'type'. Events will then be fired off for events
- against that class as well as all subclasses.
+ against that class as well as all subclasses.
'type' itself is also accepted as a target
in which case the events fire for all classes.
-
+
"""
-
+
@classmethod
def _accept_with(cls, target):
from sqlalchemy.orm.instrumentation import instrumentation_registry
-
+
if isinstance(target, type):
return instrumentation_registry
else:
@@ -41,36 +41,36 @@ class InstrumentationEvents(event.Events):
def class_instrument(self, cls):
"""Called after the given class is instrumented.
-
+
To get at the :class:`.ClassManager`, use
:func:`.manager_of_class`.
-
+
"""
def class_uninstrument(self, cls):
"""Called before the given class is uninstrumented.
-
+
To get at the :class:`.ClassManager`, use
:func:`.manager_of_class`.
-
+
"""
-
-
+
+
def attribute_instrument(self, cls, key, inst):
"""Called when an attribute is instrumented."""
class InstanceEvents(event.Events):
"""Define events specific to object lifecycle.
-
+
Instance-level don't automatically propagate their associations
to subclasses.
-
+
"""
@classmethod
def _accept_with(cls, target):
from sqlalchemy.orm.instrumentation import ClassManager, manager_of_class
from sqlalchemy.orm import Mapper, mapper
-
+
if isinstance(target, ClassManager):
return target
elif isinstance(target, Mapper):
@@ -85,7 +85,7 @@ class InstanceEvents(event.Events):
if manager:
return manager
return None
-
+
@classmethod
def _listen(cls, target, identifier, fn, raw=False, propagate=False):
if not raw:
@@ -98,7 +98,7 @@ class InstanceEvents(event.Events):
if propagate:
for mgr in target.subclass_managers(True):
event.Events._listen(mgr, identifier, fn, True)
-
+
@classmethod
def _remove(cls, identifier, target, fn):
raise NotImplementedError("Removal of instance events not yet implemented")
@@ -107,26 +107,26 @@ class InstanceEvents(event.Events):
"""Called when the first instance of a particular mapping is called.
"""
-
+
def init(self, target, args, kwargs):
"""Receive an instance when it's constructor is called.
-
+
This method is only called during a userland construction of
an object. It is not called when an object is loaded from the
database.
"""
-
+
def init_failure(self, target, args, kwargs):
"""Receive an instance when it's constructor has been called,
and raised an exception.
-
+
This method is only called during a userland construction of
an object. It is not called when an object is loaded from the
database.
"""
-
+
def load(self, target, context):
"""Receive an object instance after it has been created via
``__new__``, and after initial attribute population has
@@ -153,7 +153,7 @@ class InstanceEvents(event.Events):
def refresh(self, target, context, attrs):
"""Receive an object instance after one or more attributes have
been refreshed from a query.
-
+
:param target: the mapped instance. If
the event is configured with ``raw=True``, this will
instead be the :class:`.InstanceState` state-management
@@ -163,13 +163,13 @@ class InstanceEvents(event.Events):
: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, 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.
@@ -180,27 +180,27 @@ class InstanceEvents(event.Events):
: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.
-
+
: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):
"""Define events specific to mappings.
e.g.::
-
+
from sqlalchemy import event
def my_before_insert_listener(mapper, connection, target):
@@ -209,7 +209,7 @@ class MapperEvents(event.Events):
target.calculated_value = connection.scalar(
"select my_special_function(%d)"
% target.special_number)
-
+
# associate the listener function with SomeMappedClass,
# to execute during the "before_insert" hook
event.listen(SomeMappedClass, 'before_insert', my_before_insert_listener)
@@ -221,13 +221,13 @@ class MapperEvents(event.Events):
for global event reception::
from sqlalchemy.orm import mapper
-
+
def some_listener(mapper, connection, target):
log.debug("Instance %s being inserted" % target)
-
+
# attach to all mappers
event.listen(mapper, 'before_insert', some_listener)
-
+
Mapper events provide hooks into critical sections of the
mapper, including those related to object instrumentation,
object loading, and object persistence. In particular, the
@@ -240,10 +240,10 @@ class MapperEvents(event.Events):
:meth:`.SessionEvents.after_flush` methods as more
flexible and user-friendly hooks in which to apply
additional database state during a flush.
-
+
When using :class:`.MapperEvents`, several modifiers are
available to the :func:`.event.listen` function.
-
+
:param propagate=False: When True, the event listener should
be applied to all inheriting mappers as well as the
mapper which is the target of this listener.
@@ -256,7 +256,7 @@ class MapperEvents(event.Events):
control subsequent event propagation, or to otherwise alter
the operation in progress by the mapper. Possible return
values are:
-
+
* ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event
processing normally.
* ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent
@@ -264,7 +264,7 @@ class MapperEvents(event.Events):
* other values - the return value specified by specific listeners,
such as :meth:`~.MapperEvents.translate_row` or
:meth:`~.MapperEvents.create_instance`.
-
+
"""
@classmethod
@@ -279,7 +279,7 @@ class MapperEvents(event.Events):
return class_mapper(target)
else:
return target
-
+
@classmethod
def _listen(cls, target, identifier, fn,
raw=False, retval=False, propagate=False):
@@ -292,7 +292,7 @@ class MapperEvents(event.Events):
target_index = inspect.getargspec(meth)[0].index('target') - 1
except ValueError:
target_index = None
-
+
wrapped_fn = fn
def wrap(*arg, **kw):
if not raw and target_index is not None:
@@ -304,42 +304,42 @@ class MapperEvents(event.Events):
else:
return wrapped_fn(*arg, **kw)
fn = wrap
-
+
if propagate:
for mapper in target.self_and_descendants:
event.Events._listen(mapper, identifier, fn, propagate=True)
else:
event.Events._listen(target, identifier, fn)
-
+
def instrument_class(self, mapper, class_):
"""Receive a class when the mapper is first constructed,
before instrumentation is applied to the mapped class.
-
+
This event is the earliest phase of mapper construction.
Most attributes of the mapper are not yet initialized.
-
+
This listener can generally only be applied to the :class:`.Mapper`
class overall.
-
+
:param mapper: the :class:`.Mapper` which is the target
of this event.
:param class\_: the mapped class.
-
+
"""
-
+
def mapper_configured(self, mapper, class_):
"""Called when the mapper for the class is fully configured.
This event is the latest phase of mapper construction.
The mapper should be in its final state.
-
+
:param mapper: the :class:`.Mapper` which is the target
of this event.
:param class\_: the mapped class.
-
+
"""
# TODO: need coverage for this event
-
+
def translate_row(self, mapper, context, row):
"""Perform pre-processing on the given result row and return a
new row instance.
@@ -352,7 +352,7 @@ class MapperEvents(event.Events):
object which contains mapped columns as keys. The
returned object should also be a dictionary-like object
which recognizes mapped columns as keys.
-
+
:param mapper: the :class:`.Mapper` which is the target
of this event.
:param context: the :class:`.QueryContext`, which includes
@@ -364,8 +364,8 @@ class MapperEvents(event.Events):
:return: When configured with ``retval=True``, the function
should return a dictionary-like row object, or ``EXT_CONTINUE``,
indicating the original row should be used.
-
-
+
+
"""
def create_instance(self, mapper, context, row, class_):
@@ -396,10 +396,10 @@ class MapperEvents(event.Events):
result, **flags):
"""Receive an object instance before that instance is appended
to a result list.
-
+
This is a rarely used hook which can be used to alter
the construction of a result list returned by :class:`.Query`.
-
+
:param mapper: the :class:`.Mapper` which is the target
of this event.
:param context: the :class:`.QueryContext`, which includes
@@ -435,7 +435,7 @@ class MapperEvents(event.Events):
unloaded attributes to be populated. The method may be called
many times for a single instance, as multiple result rows are
used to populate eagerly loaded collections.
-
+
Most usages of this hook are obsolete. For a
generic "object has been newly created from a row" hook, use
:meth:`.InstanceEvents.load`.
@@ -462,12 +462,12 @@ class MapperEvents(event.Events):
def before_insert(self, mapper, connection, target):
"""Receive an object instance before an INSERT statement
is emitted corresponding to that instance.
-
+
This event is used to modify local, non-object related
attributes on the instance before an INSERT occurs, as well
as to emit additional SQL statements on the given
- connection.
-
+ connection.
+
The event is often called for a batch of objects of the
same class before their INSERT statements are emitted at
once in a later step. In the extremely rare case that
@@ -476,7 +476,7 @@ class MapperEvents(event.Events):
batches of instances to be broken up into individual
(and more poorly performing) event->persist->event
steps.
-
+
Handlers should **not** modify any attributes which are
mapped by :func:`.relationship`, nor should they attempt
to make any modifications to the :class:`.Session` in
@@ -502,11 +502,11 @@ class MapperEvents(event.Events):
def after_insert(self, mapper, connection, target):
"""Receive an object instance after an INSERT statement
is emitted corresponding to that instance.
-
+
This event is used to modify in-Python-only
state on the instance after an INSERT occurs, as well
as to emit additional SQL statements on the given
- connection.
+ connection.
The event is often called for a batch of objects of the
same class after their INSERT statements have been
@@ -528,7 +528,7 @@ class MapperEvents(event.Events):
instead be the :class:`.InstanceState` state-management
object associated with the instance.
:return: No return value is supported by this event.
-
+
"""
def before_update(self, mapper, connection, target):
@@ -538,7 +538,7 @@ class MapperEvents(event.Events):
This event is used to modify local, non-object related
attributes on the instance before an UPDATE occurs, as well
as to emit additional SQL statements on the given
- connection.
+ connection.
This method is called for all instances that are
marked as "dirty", *even those which have no net changes
@@ -553,7 +553,7 @@ class MapperEvents(event.Events):
issued, although you can affect the outcome here by
modifying attributes so that a net change in value does
exist.
-
+
To detect if the column-based attributes on the object have net
changes, and will therefore generate an UPDATE statement, use
``object_session(instance).is_modified(instance,
@@ -567,7 +567,7 @@ class MapperEvents(event.Events):
batches of instances to be broken up into individual
(and more poorly performing) event->persist->event
steps.
-
+
Handlers should **not** modify any attributes which are
mapped by :func:`.relationship`, nor should they attempt
to make any modifications to the :class:`.Session` in
@@ -596,7 +596,7 @@ class MapperEvents(event.Events):
This event is used to modify in-Python-only
state on the instance after an UPDATE occurs, as well
as to emit additional SQL statements on the given
- connection.
+ connection.
This method is called for all instances that are
marked as "dirty", *even those which have no net changes
@@ -610,7 +610,7 @@ class MapperEvents(event.Events):
being sent to :meth:`~.MapperEvents.after_update` is
*not* a guarantee that an UPDATE statement has been
issued.
-
+
To detect if the column-based attributes on the object have net
changes, and therefore resulted in an UPDATE statement, use
``object_session(instance).is_modified(instance,
@@ -624,7 +624,7 @@ class MapperEvents(event.Events):
batches of instances to be broken up into individual
(and more poorly performing) event->persist->event
steps.
-
+
:param mapper: the :class:`.Mapper` which is the target
of this event.
:param connection: the :class:`.Connection` being used to
@@ -636,21 +636,21 @@ class MapperEvents(event.Events):
instead be the :class:`.InstanceState` state-management
object associated with the instance.
:return: No return value is supported by this event.
-
+
"""
def before_delete(self, mapper, connection, target):
"""Receive an object instance before a DELETE statement
is emitted corresponding to that instance.
-
+
This event is used to emit additional SQL statements on
the given connection as well as to perform application
specific bookkeeping related to a deletion event.
-
+
The event is often called for a batch of objects of the
same class before their DELETE statements are emitted at
once in a later step.
-
+
Handlers should **not** modify any attributes which are
mapped by :func:`.relationship`, nor should they attempt
to make any modifications to the :class:`.Session` in
@@ -670,17 +670,17 @@ class MapperEvents(event.Events):
instead be the :class:`.InstanceState` state-management
object associated with the instance.
:return: No return value is supported by this event.
-
+
"""
def after_delete(self, mapper, connection, target):
"""Receive an object instance after a DELETE statement
has been emitted corresponding to that instance.
-
+
This event is used to emit additional SQL statements on
the given connection as well as to perform application
specific bookkeeping related to a deletion event.
-
+
The event is often called for a batch of objects of the
same class after their DELETE statements have been emitted at
once in a previous step.
@@ -696,36 +696,36 @@ class MapperEvents(event.Events):
instead be the :class:`.InstanceState` state-management
object associated with the instance.
:return: No return value is supported by this event.
-
+
"""
@classmethod
def _remove(cls, identifier, target, fn):
raise NotImplementedError("Removal of mapper events not yet implemented")
-
+
class SessionEvents(event.Events):
"""Define events specific to :class:`.Session` lifecycle.
-
+
e.g.::
-
+
from sqlalchemy import event
from sqlalchemy.orm import sessionmaker
-
+
class my_before_commit(session):
print "before commit!"
-
+
Session = sessionmaker()
-
+
event.listen(Session, "before_commit", my_before_commit)
-
+
The :func:`~.event.listen` function will accept
:class:`.Session` objects as well as the return result
of :func:`.sessionmaker` and :func:`.scoped_session`.
-
+
Additionally, it accepts the :class:`.Session` class which
will apply listeners to all :class:`.Session` instances
globally.
-
+
"""
@classmethod
@@ -748,39 +748,39 @@ class SessionEvents(event.Events):
return target
else:
return None
-
+
@classmethod
def _remove(cls, identifier, target, fn):
raise NotImplementedError("Removal of session events not yet implemented")
def before_commit(self, session):
"""Execute before commit is called.
-
+
Note that this may not be per-flush if a longer running
transaction is ongoing."""
def after_commit(self, session):
"""Execute after a commit has occured.
-
+
Note that this may not be per-flush if a longer running
transaction is ongoing."""
def after_rollback(self, session):
"""Execute after a rollback has occured.
-
+
Note that this may not be per-flush if a longer running
transaction is ongoing."""
def before_flush( self, session, flush_context, instances):
"""Execute before flush process has started.
-
+
`instances` is an optional list of objects which were passed to
the ``flush()`` method. """
def after_flush(self, session, flush_context):
"""Execute after flush has completed, but before commit has been
called.
-
+
Note that the session's state is still in pre-flush, i.e. 'new',
'dirty', and 'deleted' lists still show pre-flush state as well
as the history settings on instance attributes."""
@@ -788,7 +788,7 @@ class SessionEvents(event.Events):
def after_flush_postexec(self, session, flush_context):
"""Execute after flush has completed, and after the post-exec
state occurs.
-
+
This will be when the 'new', 'dirty', and 'deleted' lists are in
their final state. An actual commit() may or may not have
occured, depending on whether or not the flush started its own
@@ -796,20 +796,20 @@ class SessionEvents(event.Events):
def after_begin( self, session, transaction, connection):
"""Execute after a transaction is begun on a connection
-
+
`transaction` is the SessionTransaction. This method is called
after an engine level transaction is begun on a connection. """
def after_attach(self, session, instance):
"""Execute after an instance is attached to a session.
-
+
This is called after an add, delete or merge. """
def after_bulk_update( self, session, query, query_context, result):
"""Execute after a bulk update operation to the session.
-
+
This is called after a session.query(...).update()
-
+
`query` is the query object that this update operation was
called on. `query_context` was the query context object.
`result` is the result object returned from the bulk operation.
@@ -817,9 +817,9 @@ class SessionEvents(event.Events):
def after_bulk_delete( self, session, query, query_context, result):
"""Execute after a bulk delete operation to the session.
-
+
This is called after a session.query(...).delete()
-
+
`query` is the query object that this delete operation was
called on. `query_context` was the query context object.
`result` is the result object returned from the bulk operation.
@@ -828,37 +828,37 @@ class SessionEvents(event.Events):
class AttributeEvents(event.Events):
"""Define events for object attributes.
-
+
These are typically defined on the class-bound descriptor for the
target class.
e.g.::
-
+
from sqlalchemy import event
-
+
def my_append_listener(target, value, initiator):
print "received append event for target: %s" % target
-
+
event.listen(MyClass.collection, 'append', my_append_listener)
-
+
Listeners have the option to return a possibly modified version
of the value, when the ``retval=True`` flag is passed
to :func:`~.event.listen`::
-
+
def validate_phone(target, value, oldvalue, initiator):
"Strip non-numeric characters from a phone number"
-
+
return re.sub(r'(?![0-9])', '', value)
-
+
# setup listener on UserContact.phone attribute, instructing
# it to use the return value
listen(UserContact.phone, 'set', validate_phone, retval=True)
-
+
A validation function like the above can also raise an exception
such as :class:`ValueError` to halt the operation.
-
+
Several modifiers are available to the :func:`~.event.listen` function.
-
+
:param active_history=False: When True, indicates that the
"set" event would like to receive the "old" value being
replaced unconditionally, even if this requires firing off
@@ -879,8 +879,8 @@ class AttributeEvents(event.Events):
listening must return the "value" argument from the
function. This gives the listening function the opportunity
to change the value that is ultimately used for a "set"
- or "append" event.
-
+ or "append" event.
+
"""
@classmethod
@@ -891,17 +891,17 @@ class AttributeEvents(event.Events):
return getattr(target.parent.class_, target.key)
else:
return target
-
+
@classmethod
def _listen(cls, target, identifier, fn, active_history=False,
raw=False, retval=False,
propagate=False):
if active_history:
target.dispatch._active_history = True
-
+
# TODO: for removal, need to package the identity
# of the wrapper with the original function.
-
+
if not raw or not retval:
orig_fn = fn
def wrap(target, value, *arg):
@@ -913,21 +913,21 @@ class AttributeEvents(event.Events):
else:
return orig_fn(target, value, *arg)
fn = wrap
-
+
event.Events._listen(target, identifier, fn, propagate)
-
+
if propagate:
from sqlalchemy.orm.instrumentation import manager_of_class
-
+
manager = manager_of_class(target.class_)
-
+
for mgr in manager.subclass_managers(True):
event.Events._listen(mgr[target.key], identifier, fn, True)
-
+
@classmethod
def _remove(cls, identifier, target, fn):
raise NotImplementedError("Removal of attribute events not yet implemented")
-
+
def append(self, target, value, initiator):
"""Receive a collection append event.
@@ -942,7 +942,7 @@ class AttributeEvents(event.Events):
which initiated this event.
:return: if the event was registered with ``retval=True``,
the given value, or a new effective value, should be returned.
-
+
"""
def remove(self, target, value, initiator):