2
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
4
# This module is part of SQLAlchemy and is released under
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
7
"""ORM event interfaces.
10
from sqlalchemy import event, exc
13
class InstrumentationEvents(event.Events):
14
"""Events related to class instrumentation events.
16
The listeners here support being established against
17
any new style class, that is any object that is a subclass
18
of 'type'. Events will then be fired off for events
19
against that class as well as all subclasses.
20
'type' itself is also accepted as a target
21
in which case the events fire for all classes.
26
def _accept_with(cls, target):
27
from sqlalchemy.orm.instrumentation import instrumentation_registry
29
if isinstance(target, type):
30
return instrumentation_registry
35
def _listen(cls, target, identifier, fn, propagate=False):
36
event.Events._listen(target, identifier, fn, propagate=propagate)
39
def _remove(cls, identifier, target, fn):
40
raise NotImplementedError("Removal of instrumentation events not yet implemented")
42
def class_instrument(self, cls):
43
"""Called after the given class is instrumented.
45
To get at the :class:`.ClassManager`, use
46
:func:`.manager_of_class`.
50
def class_uninstrument(self, cls):
51
"""Called before the given class is uninstrumented.
53
To get at the :class:`.ClassManager`, use
54
:func:`.manager_of_class`.
59
def attribute_instrument(self, cls, key, inst):
60
"""Called when an attribute is instrumented."""
62
class InstanceEvents(event.Events):
63
"""Define events specific to object lifecycle.
67
from sqlalchemy import event
69
def my_load_listener(target, context):
72
event.listen(SomeMappedClass, 'load', my_load_listener)
74
Available targets include mapped classes, instances of
75
:class:`.Mapper` (i.e. returned by :func:`.mapper`,
76
:func:`.class_mapper` and similar), as well as the
77
:class:`.Mapper` class and :func:`.mapper` function itself
78
for global event reception::
80
from sqlalchemy.orm import mapper
82
def some_listener(target, context):
83
log.debug("Instance %s being loaded" % target)
85
# attach to all mappers
86
event.listen(mapper, 'load', some_listener)
88
Instance events are closely related to mapper events, but
89
are more specific to the instance and its instrumentation,
90
rather than its system of persistence.
92
When using :class:`.InstanceEvents`, several modifiers are
93
available to the :func:`.event.listen` function.
95
:param propagate=False: When True, the event listener should
96
be applied to all inheriting mappers as well as the
97
mapper which is the target of this listener.
98
:param raw=False: When True, the "target" argument passed
99
to applicable event listener functions will be the
100
instance's :class:`.InstanceState` management
101
object, rather than the mapped instance itself.
105
def _accept_with(cls, target):
106
from sqlalchemy.orm.instrumentation import ClassManager, manager_of_class
107
from sqlalchemy.orm import Mapper, mapper
109
if isinstance(target, ClassManager):
111
elif isinstance(target, Mapper):
112
return target.class_manager
113
elif target is mapper:
115
elif isinstance(target, type):
116
if issubclass(target, Mapper):
119
manager = manager_of_class(target)
125
def _listen(cls, target, identifier, fn, raw=False, propagate=False):
128
def wrap(state, *arg, **kw):
129
return orig_fn(state.obj(), *arg, **kw)
132
event.Events._listen(target, identifier, fn, propagate=propagate)
134
for mgr in target.subclass_managers(True):
135
event.Events._listen(mgr, identifier, fn, True)
138
def _remove(cls, identifier, target, fn):
139
raise NotImplementedError("Removal of instance events not yet implemented")
141
def first_init(self, manager, cls):
142
"""Called when the first instance of a particular mapping is called.
146
def init(self, target, args, kwargs):
147
"""Receive an instance when it's constructor is called.
149
This method is only called during a userland construction of
150
an object. It is not called when an object is loaded from the
155
def init_failure(self, target, args, kwargs):
156
"""Receive an instance when it's constructor has been called,
157
and raised an exception.
159
This method is only called during a userland construction of
160
an object. It is not called when an object is loaded from the
165
def load(self, target, context):
166
"""Receive an object instance after it has been created via
167
``__new__``, and after initial attribute population has
170
This typically occurs when the instance is created based on
171
incoming result rows, and is only called once for that
174
Note that during a result-row load, this method is called upon
175
the first row received for this instance. Note that some
176
attributes and collections may or may not be loaded or even
177
initialized, depending on what's present in the result rows.
179
:param target: the mapped instance. If
180
the event is configured with ``raw=True``, this will
181
instead be the :class:`.InstanceState` state-management
182
object associated with the instance.
183
:param context: the :class:`.QueryContext` corresponding to the
184
current :class:`.Query` in progress. This argument may be
185
``None`` if the load does not correspond to a :class:`.Query`,
186
such as during :meth:`.Session.merge`.
190
def refresh(self, target, context, attrs):
191
"""Receive an object instance after one or more attributes have
192
been refreshed from a query.
194
:param target: the mapped instance. If
195
the event is configured with ``raw=True``, this will
196
instead be the :class:`.InstanceState` state-management
197
object associated with the instance.
198
:param context: the :class:`.QueryContext` corresponding to the
199
current :class:`.Query` in progress.
200
:param attrs: iterable collection of attribute names which
201
were populated, or None if all column-mapped, non-deferred
202
attributes were populated.
206
def expire(self, target, attrs):
207
"""Receive an object instance after its attributes or some subset
210
'keys' is a list of attribute names. If None, the entire
213
:param target: the mapped instance. If
214
the event is configured with ``raw=True``, this will
215
instead be the :class:`.InstanceState` state-management
216
object associated with the instance.
217
:param attrs: iterable collection of attribute
218
names which were expired, or None if all attributes were
223
def resurrect(self, target):
224
"""Receive an object instance as it is 'resurrected' from
225
garbage collection, which occurs when a "dirty" state falls
228
:param target: the mapped instance. If
229
the event is configured with ``raw=True``, this will
230
instead be the :class:`.InstanceState` state-management
231
object associated with the instance.
235
def pickle(self, target, state_dict):
236
"""Receive an object instance when its associated state is
239
:param target: the mapped instance. If
240
the event is configured with ``raw=True``, this will
241
instead be the :class:`.InstanceState` state-management
242
object associated with the instance.
243
:param state_dict: the dictionary returned by
244
:class:`.InstanceState.__getstate__`, containing the state
249
def unpickle(self, target, state_dict):
250
"""Receive an object instance after it's associated state has
253
:param target: the mapped instance. If
254
the event is configured with ``raw=True``, this will
255
instead be the :class:`.InstanceState` state-management
256
object associated with the instance.
257
:param state_dict: the dictionary sent to
258
:class:`.InstanceState.__setstate__`, containing the state
259
dictionary which was pickled.
263
class MapperEvents(event.Events):
264
"""Define events specific to mappings.
268
from sqlalchemy import event
270
def my_before_insert_listener(mapper, connection, target):
271
# execute a stored procedure upon INSERT,
272
# apply the value to the row to be inserted
273
target.calculated_value = connection.scalar(
274
"select my_special_function(%d)"
275
% target.special_number)
277
# associate the listener function with SomeMappedClass,
278
# to execute during the "before_insert" hook
279
event.listen(SomeMappedClass, 'before_insert', my_before_insert_listener)
281
Available targets include mapped classes, instances of
282
:class:`.Mapper` (i.e. returned by :func:`.mapper`,
283
:func:`.class_mapper` and similar), as well as the
284
:class:`.Mapper` class and :func:`.mapper` function itself
285
for global event reception::
287
from sqlalchemy.orm import mapper
289
def some_listener(mapper, connection, target):
290
log.debug("Instance %s being inserted" % target)
292
# attach to all mappers
293
event.listen(mapper, 'before_insert', some_listener)
295
Mapper events provide hooks into critical sections of the
296
mapper, including those related to object instrumentation,
297
object loading, and object persistence. In particular, the
298
persistence methods :meth:`~.MapperEvents.before_insert`,
299
and :meth:`~.MapperEvents.before_update` are popular
300
places to augment the state being persisted - however, these
301
methods operate with several significant restrictions. The
302
user is encouraged to evaluate the
303
:meth:`.SessionEvents.before_flush` and
304
:meth:`.SessionEvents.after_flush` methods as more
305
flexible and user-friendly hooks in which to apply
306
additional database state during a flush.
308
When using :class:`.MapperEvents`, several modifiers are
309
available to the :func:`.event.listen` function.
311
:param propagate=False: When True, the event listener should
312
be applied to all inheriting mappers as well as the
313
mapper which is the target of this listener.
314
:param raw=False: When True, the "target" argument passed
315
to applicable event listener functions will be the
316
instance's :class:`.InstanceState` management
317
object, rather than the mapped instance itself.
318
:param retval=False: when True, the user-defined event function
319
must have a return value, the purpose of which is either to
320
control subsequent event propagation, or to otherwise alter
321
the operation in progress by the mapper. Possible return
324
* ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event
326
* ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent
327
event handlers in the chain.
328
* other values - the return value specified by specific listeners,
329
such as :meth:`~.MapperEvents.translate_row` or
330
:meth:`~.MapperEvents.create_instance`.
335
def _accept_with(cls, target):
336
from sqlalchemy.orm import mapper, class_mapper, Mapper
339
elif isinstance(target, type):
340
if issubclass(target, Mapper):
343
return class_mapper(target)
348
def _listen(cls, target, identifier, fn,
349
raw=False, retval=False, propagate=False):
350
from sqlalchemy.orm.interfaces import EXT_CONTINUE
352
if not raw or not retval:
354
meth = getattr(cls, identifier)
356
target_index = inspect.getargspec(meth)[0].index('target') - 1
361
def wrap(*arg, **kw):
362
if not raw and target_index is not None:
364
arg[target_index] = arg[target_index].obj()
366
wrapped_fn(*arg, **kw)
369
return wrapped_fn(*arg, **kw)
373
for mapper in target.self_and_descendants:
374
event.Events._listen(mapper, identifier, fn, propagate=True)
376
event.Events._listen(target, identifier, fn)
378
def instrument_class(self, mapper, class_):
379
"""Receive a class when the mapper is first constructed,
380
before instrumentation is applied to the mapped class.
382
This event is the earliest phase of mapper construction.
383
Most attributes of the mapper are not yet initialized.
385
This listener can generally only be applied to the :class:`.Mapper`
388
:param mapper: the :class:`.Mapper` which is the target
390
:param class\_: the mapped class.
394
def mapper_configured(self, mapper, class_):
395
"""Called when the mapper for the class is fully configured.
397
This event is the latest phase of mapper construction.
398
The mapper should be in its final state.
400
:param mapper: the :class:`.Mapper` which is the target
402
:param class\_: the mapped class.
405
# TODO: need coverage for this event
407
def translate_row(self, mapper, context, row):
408
"""Perform pre-processing on the given result row and return a
411
This listener is typically registered with ``retval=True``.
412
It is called when the mapper first receives a row, before
413
the object identity or the instance itself has been derived
414
from that row. The given row may or may not be a
415
:class:`.RowProxy` object - it will always be a dictionary-like
416
object which contains mapped columns as keys. The
417
returned object should also be a dictionary-like object
418
which recognizes mapped columns as keys.
420
:param mapper: the :class:`.Mapper` which is the target
422
:param context: the :class:`.QueryContext`, which includes
423
a handle to the current :class:`.Query` in progress as well
424
as additional state information.
425
:param row: the result row being handled. This may be
426
an actual :class:`.RowProxy` or may be a dictionary containing
427
:class:`.Column` objects as keys.
428
:return: When configured with ``retval=True``, the function
429
should return a dictionary-like row object, or ``EXT_CONTINUE``,
430
indicating the original row should be used.
435
def create_instance(self, mapper, context, row, class_):
436
"""Receive a row when a new object instance is about to be
437
created from that row.
439
The method can choose to create the instance itself, or it can return
440
EXT_CONTINUE to indicate normal object creation should take place.
441
This listener is typically registered with ``retval=True``.
443
:param mapper: the :class:`.Mapper` which is the target
445
:param context: the :class:`.QueryContext`, which includes
446
a handle to the current :class:`.Query` in progress as well
447
as additional state information.
448
:param row: the result row being handled. This may be
449
an actual :class:`.RowProxy` or may be a dictionary containing
450
:class:`.Column` objects as keys.
451
:param class\_: the mapped class.
452
:return: When configured with ``retval=True``, the return value
453
should be a newly created instance of the mapped class,
454
or ``EXT_CONTINUE`` indicating that default object construction
459
def append_result(self, mapper, context, row, target,
461
"""Receive an object instance before that instance is appended
464
This is a rarely used hook which can be used to alter
465
the construction of a result list returned by :class:`.Query`.
467
:param mapper: the :class:`.Mapper` which is the target
469
:param context: the :class:`.QueryContext`, which includes
470
a handle to the current :class:`.Query` in progress as well
471
as additional state information.
472
:param row: the result row being handled. This may be
473
an actual :class:`.RowProxy` or may be a dictionary containing
474
:class:`.Column` objects as keys.
475
:param target: the mapped instance being populated. If
476
the event is configured with ``raw=True``, this will
477
instead be the :class:`.InstanceState` state-management
478
object associated with the instance.
479
:param result: a list-like object where results are being
481
:param \**flags: Additional state information about the
482
current handling of the row.
483
:return: If this method is registered with ``retval=True``,
484
a return value of ``EXT_STOP`` will prevent the instance
485
from being appended to the given result list, whereas a
486
return value of ``EXT_CONTINUE`` will result in the default
487
behavior of appending the value to the result list.
492
def populate_instance(self, mapper, context, row,
494
"""Receive an instance before that instance has
495
its attributes populated.
497
This usually corresponds to a newly loaded instance but may
498
also correspond to an already-loaded instance which has
499
unloaded attributes to be populated. The method may be called
500
many times for a single instance, as multiple result rows are
501
used to populate eagerly loaded collections.
503
Most usages of this hook are obsolete. For a
504
generic "object has been newly created from a row" hook, use
505
:meth:`.InstanceEvents.load`.
507
:param mapper: the :class:`.Mapper` which is the target
509
:param context: the :class:`.QueryContext`, which includes
510
a handle to the current :class:`.Query` in progress as well
511
as additional state information.
512
:param row: the result row being handled. This may be
513
an actual :class:`.RowProxy` or may be a dictionary containing
514
:class:`.Column` objects as keys.
515
:param target: the mapped instance. If
516
the event is configured with ``raw=True``, this will
517
instead be the :class:`.InstanceState` state-management
518
object associated with the instance.
519
:return: When configured with ``retval=True``, a return
520
value of ``EXT_STOP`` will bypass instance population by
521
the mapper. A value of ``EXT_CONTINUE`` indicates that
522
default instance population should take place.
526
def before_insert(self, mapper, connection, target):
527
"""Receive an object instance before an INSERT statement
528
is emitted corresponding to that instance.
530
This event is used to modify local, non-object related
531
attributes on the instance before an INSERT occurs, as well
532
as to emit additional SQL statements on the given
535
The event is often called for a batch of objects of the
536
same class before their INSERT statements are emitted at
537
once in a later step. In the extremely rare case that
538
this is not desirable, the :func:`.mapper` can be
539
configured with ``batch=False``, which will cause
540
batches of instances to be broken up into individual
541
(and more poorly performing) event->persist->event
544
Handlers should **not** modify any attributes which are
545
mapped by :func:`.relationship`, nor should they attempt
546
to make any modifications to the :class:`.Session` in
547
this hook (including :meth:`.Session.add`,
548
:meth:`.Session.delete`, etc.) - such changes will not
549
take effect. For overall changes to the "flush plan",
550
use :meth:`.SessionEvents.before_flush`.
552
:param mapper: the :class:`.Mapper` which is the target
554
:param connection: the :class:`.Connection` being used to
555
emit INSERT statements for this instance. This
556
provides a handle into the current transaction on the
557
target database specific to this instance.
558
:param target: the mapped instance being persisted. If
559
the event is configured with ``raw=True``, this will
560
instead be the :class:`.InstanceState` state-management
561
object associated with the instance.
562
:return: No return value is supported by this event.
566
def after_insert(self, mapper, connection, target):
567
"""Receive an object instance after an INSERT statement
568
is emitted corresponding to that instance.
570
This event is used to modify in-Python-only
571
state on the instance after an INSERT occurs, as well
572
as to emit additional SQL statements on the given
575
The event is often called for a batch of objects of the
576
same class after their INSERT statements have been
577
emitted at once in a previous step. In the extremely
578
rare case that this is not desirable, the
579
:func:`.mapper` can be configured with ``batch=False``,
580
which will cause batches of instances to be broken up
581
into individual (and more poorly performing)
582
event->persist->event steps.
584
:param mapper: the :class:`.Mapper` which is the target
586
:param connection: the :class:`.Connection` being used to
587
emit INSERT statements for this instance. This
588
provides a handle into the current transaction on the
589
target database specific to this instance.
590
:param target: the mapped instance being persisted. If
591
the event is configured with ``raw=True``, this will
592
instead be the :class:`.InstanceState` state-management
593
object associated with the instance.
594
:return: No return value is supported by this event.
598
def before_update(self, mapper, connection, target):
599
"""Receive an object instance before an UPDATE statement
600
is emitted corresponding to that instance.
602
This event is used to modify local, non-object related
603
attributes on the instance before an UPDATE occurs, as well
604
as to emit additional SQL statements on the given
607
This method is called for all instances that are
608
marked as "dirty", *even those which have no net changes
609
to their column-based attributes*. An object is marked
610
as dirty when any of its column-based attributes have a
611
"set attribute" operation called or when any of its
612
collections are modified. If, at update time, no
613
column-based attributes have any net changes, no UPDATE
614
statement will be issued. This means that an instance
615
being sent to :meth:`~.MapperEvents.before_update` is
616
*not* a guarantee that an UPDATE statement will be
617
issued, although you can affect the outcome here by
618
modifying attributes so that a net change in value does
621
To detect if the column-based attributes on the object have net
622
changes, and will therefore generate an UPDATE statement, use
623
``object_session(instance).is_modified(instance,
624
include_collections=False)``.
626
The event is often called for a batch of objects of the
627
same class before their UPDATE statements are emitted at
628
once in a later step. In the extremely rare case that
629
this is not desirable, the :func:`.mapper` can be
630
configured with ``batch=False``, which will cause
631
batches of instances to be broken up into individual
632
(and more poorly performing) event->persist->event
635
Handlers should **not** modify any attributes which are
636
mapped by :func:`.relationship`, nor should they attempt
637
to make any modifications to the :class:`.Session` in
638
this hook (including :meth:`.Session.add`,
639
:meth:`.Session.delete`, etc.) - such changes will not
640
take effect. For overall changes to the "flush plan",
641
use :meth:`.SessionEvents.before_flush`.
643
:param mapper: the :class:`.Mapper` which is the target
645
:param connection: the :class:`.Connection` being used to
646
emit UPDATE statements for this instance. This
647
provides a handle into the current transaction on the
648
target database specific to this instance.
649
:param target: the mapped instance being persisted. If
650
the event is configured with ``raw=True``, this will
651
instead be the :class:`.InstanceState` state-management
652
object associated with the instance.
653
:return: No return value is supported by this event.
656
def after_update(self, mapper, connection, target):
657
"""Receive an object instance after an UPDATE statement
658
is emitted corresponding to that instance.
660
This event is used to modify in-Python-only
661
state on the instance after an UPDATE occurs, as well
662
as to emit additional SQL statements on the given
665
This method is called for all instances that are
666
marked as "dirty", *even those which have no net changes
667
to their column-based attributes*, and for which
668
no UPDATE statement has proceeded. An object is marked
669
as dirty when any of its column-based attributes have a
670
"set attribute" operation called or when any of its
671
collections are modified. If, at update time, no
672
column-based attributes have any net changes, no UPDATE
673
statement will be issued. This means that an instance
674
being sent to :meth:`~.MapperEvents.after_update` is
675
*not* a guarantee that an UPDATE statement has been
678
To detect if the column-based attributes on the object have net
679
changes, and therefore resulted in an UPDATE statement, use
680
``object_session(instance).is_modified(instance,
681
include_collections=False)``.
683
The event is often called for a batch of objects of the
684
same class after their UPDATE statements have been emitted at
685
once in a previous step. In the extremely rare case that
686
this is not desirable, the :func:`.mapper` can be
687
configured with ``batch=False``, which will cause
688
batches of instances to be broken up into individual
689
(and more poorly performing) event->persist->event
692
:param mapper: the :class:`.Mapper` which is the target
694
:param connection: the :class:`.Connection` being used to
695
emit UPDATE statements for this instance. This
696
provides a handle into the current transaction on the
697
target database specific to this instance.
698
:param target: the mapped instance being persisted. If
699
the event is configured with ``raw=True``, this will
700
instead be the :class:`.InstanceState` state-management
701
object associated with the instance.
702
:return: No return value is supported by this event.
706
def before_delete(self, mapper, connection, target):
707
"""Receive an object instance before a DELETE statement
708
is emitted corresponding to that instance.
710
This event is used to emit additional SQL statements on
711
the given connection as well as to perform application
712
specific bookkeeping related to a deletion event.
714
The event is often called for a batch of objects of the
715
same class before their DELETE statements are emitted at
716
once in a later step.
718
Handlers should **not** modify any attributes which are
719
mapped by :func:`.relationship`, nor should they attempt
720
to make any modifications to the :class:`.Session` in
721
this hook (including :meth:`.Session.add`,
722
:meth:`.Session.delete`, etc.) - such changes will not
723
take effect. For overall changes to the "flush plan",
724
use :meth:`.SessionEvents.before_flush`.
726
:param mapper: the :class:`.Mapper` which is the target
728
:param connection: the :class:`.Connection` being used to
729
emit DELETE statements for this instance. This
730
provides a handle into the current transaction on the
731
target database specific to this instance.
732
:param target: the mapped instance being deleted. If
733
the event is configured with ``raw=True``, this will
734
instead be the :class:`.InstanceState` state-management
735
object associated with the instance.
736
:return: No return value is supported by this event.
740
def after_delete(self, mapper, connection, target):
741
"""Receive an object instance after a DELETE statement
742
has been emitted corresponding to that instance.
744
This event is used to emit additional SQL statements on
745
the given connection as well as to perform application
746
specific bookkeeping related to a deletion event.
748
The event is often called for a batch of objects of the
749
same class after their DELETE statements have been emitted at
750
once in a previous step.
752
:param mapper: the :class:`.Mapper` which is the target
754
:param connection: the :class:`.Connection` being used to
755
emit DELETE statements for this instance. This
756
provides a handle into the current transaction on the
757
target database specific to this instance.
758
:param target: the mapped instance being deleted. If
759
the event is configured with ``raw=True``, this will
760
instead be the :class:`.InstanceState` state-management
761
object associated with the instance.
762
:return: No return value is supported by this event.
767
def _remove(cls, identifier, target, fn):
768
raise NotImplementedError("Removal of mapper events not yet implemented")
770
class SessionEvents(event.Events):
771
"""Define events specific to :class:`.Session` lifecycle.
775
from sqlalchemy import event
776
from sqlalchemy.orm import sessionmaker
778
def my_before_commit(session):
779
print "before commit!"
781
Session = sessionmaker()
783
event.listen(Session, "before_commit", my_before_commit)
785
The :func:`~.event.listen` function will accept
786
:class:`.Session` objects as well as the return result
787
of :func:`.sessionmaker` and :func:`.scoped_session`.
789
Additionally, it accepts the :class:`.Session` class which
790
will apply listeners to all :class:`.Session` instances
796
def _accept_with(cls, target):
797
from sqlalchemy.orm import ScopedSession, Session
798
if isinstance(target, ScopedSession):
799
if not isinstance(target.session_factory, type) or \
800
not issubclass(target.session_factory, Session):
801
raise exc.ArgumentError(
802
"Session event listen on a ScopedSession "
803
"requires that its creation callable "
804
"is a Session subclass.")
805
return target.session_factory
806
elif isinstance(target, type):
807
if issubclass(target, ScopedSession):
809
elif issubclass(target, Session):
811
elif isinstance(target, Session):
817
def _remove(cls, identifier, target, fn):
818
raise NotImplementedError("Removal of session events not yet implemented")
820
def before_commit(self, session):
821
"""Execute before commit is called.
823
Note that this may not be per-flush if a longer running
824
transaction is ongoing."""
826
def after_commit(self, session):
827
"""Execute after a commit has occurred.
829
Note that this may not be per-flush if a longer running
830
transaction is ongoing."""
832
def after_rollback(self, session):
833
"""Execute after a rollback has occurred.
835
Note that this may not be per-flush if a longer running
836
transaction is ongoing."""
838
def before_flush( self, session, flush_context, instances):
839
"""Execute before flush process has started.
841
`instances` is an optional list of objects which were passed to
842
the ``flush()`` method. """
844
def after_flush(self, session, flush_context):
845
"""Execute after flush has completed, but before commit has been
848
Note that the session's state is still in pre-flush, i.e. 'new',
849
'dirty', and 'deleted' lists still show pre-flush state as well
850
as the history settings on instance attributes."""
852
def after_flush_postexec(self, session, flush_context):
853
"""Execute after flush has completed, and after the post-exec
856
This will be when the 'new', 'dirty', and 'deleted' lists are in
857
their final state. An actual commit() may or may not have
858
occurred, depending on whether or not the flush started its own
859
transaction or participated in a larger transaction. """
861
def after_begin( self, session, transaction, connection):
862
"""Execute after a transaction is begun on a connection
864
`transaction` is the SessionTransaction. This method is called
865
after an engine level transaction is begun on a connection. """
867
def after_attach(self, session, instance):
868
"""Execute after an instance is attached to a session.
870
This is called after an add, delete or merge. """
872
def after_bulk_update( self, session, query, query_context, result):
873
"""Execute after a bulk update operation to the session.
875
This is called after a session.query(...).update()
877
`query` is the query object that this update operation was
878
called on. `query_context` was the query context object.
879
`result` is the result object returned from the bulk operation.
882
def after_bulk_delete( self, session, query, query_context, result):
883
"""Execute after a bulk delete operation to the session.
885
This is called after a session.query(...).delete()
887
`query` is the query object that this delete operation was
888
called on. `query_context` was the query context object.
889
`result` is the result object returned from the bulk operation.
893
class AttributeEvents(event.Events):
894
"""Define events for object attributes.
896
These are typically defined on the class-bound descriptor for the
901
from sqlalchemy import event
903
def my_append_listener(target, value, initiator):
904
print "received append event for target: %s" % target
906
event.listen(MyClass.collection, 'append', my_append_listener)
908
Listeners have the option to return a possibly modified version
909
of the value, when the ``retval=True`` flag is passed
910
to :func:`~.event.listen`::
912
def validate_phone(target, value, oldvalue, initiator):
913
"Strip non-numeric characters from a phone number"
915
return re.sub(r'(?![0-9])', '', value)
917
# setup listener on UserContact.phone attribute, instructing
918
# it to use the return value
919
listen(UserContact.phone, 'set', validate_phone, retval=True)
921
A validation function like the above can also raise an exception
922
such as :class:`.ValueError` to halt the operation.
924
Several modifiers are available to the :func:`~.event.listen` function.
926
:param active_history=False: When True, indicates that the
927
"set" event would like to receive the "old" value being
928
replaced unconditionally, even if this requires firing off
929
database loads. Note that ``active_history`` can also be
930
set directly via :func:`.column_property` and
931
:func:`.relationship`.
933
:param propagate=False: When True, the listener function will
934
be established not just for the class attribute given, but
935
for attributes of the same name on all current subclasses
936
of that class, as well as all future subclasses of that
937
class, using an additional listener that listens for
938
instrumentation events.
939
:param raw=False: When True, the "target" argument to the
940
event will be the :class:`.InstanceState` management
941
object, rather than the mapped instance itself.
942
:param retval=False: when True, the user-defined event
943
listening must return the "value" argument from the
944
function. This gives the listening function the opportunity
945
to change the value that is ultimately used for a "set"
951
def _accept_with(cls, target):
952
from sqlalchemy.orm import interfaces
954
if isinstance(target, interfaces.MapperProperty):
955
return getattr(target.parent.class_, target.key)
960
def _listen(cls, target, identifier, fn, active_history=False,
961
raw=False, retval=False,
964
target.dispatch._active_history = True
966
# TODO: for removal, need to package the identity
967
# of the wrapper with the original function.
969
if not raw or not retval:
971
def wrap(target, value, *arg):
973
target = target.obj()
975
orig_fn(target, value, *arg)
978
return orig_fn(target, value, *arg)
981
event.Events._listen(target, identifier, fn, propagate)
984
from sqlalchemy.orm.instrumentation import manager_of_class
986
manager = manager_of_class(target.class_)
988
for mgr in manager.subclass_managers(True):
989
event.Events._listen(mgr[target.key], identifier, fn, True)
992
def _remove(cls, identifier, target, fn):
993
raise NotImplementedError("Removal of attribute events not yet implemented")
995
def append(self, target, value, initiator):
996
"""Receive a collection append event.
998
:param target: the object instance receiving the event.
999
If the listener is registered with ``raw=True``, this will
1000
be the :class:`.InstanceState` object.
1001
:param value: the value being appended. If this listener
1002
is registered with ``retval=True``, the listener
1003
function must return this value, or a new value which
1005
:param initiator: the attribute implementation object
1006
which initiated this event.
1007
:return: if the event was registered with ``retval=True``,
1008
the given value, or a new effective value, should be returned.
1012
def remove(self, target, value, initiator):
1013
"""Receive a collection remove event.
1015
:param target: the object instance receiving the event.
1016
If the listener is registered with ``raw=True``, this will
1017
be the :class:`.InstanceState` object.
1018
:param value: the value being removed.
1019
:param initiator: the attribute implementation object
1020
which initiated this event.
1021
:return: No return value is defined for this event.
1024
def set(self, target, value, oldvalue, initiator):
1025
"""Receive a scalar set event.
1027
:param target: the object instance receiving the event.
1028
If the listener is registered with ``raw=True``, this will
1029
be the :class:`.InstanceState` object.
1030
:param value: the value being set. If this listener
1031
is registered with ``retval=True``, the listener
1032
function must return this value, or a new value which
1034
:param oldvalue: the previous value being replaced. This
1035
may also be the symbol ``NEVER_SET`` or ``NO_VALUE``.
1036
If the listener is registered with ``active_history=True``,
1037
the previous value of the attribute will be loaded from
1038
the database if the existing value is currently unloaded
1040
:param initiator: the attribute implementation object
1041
which initiated this event.
1042
:return: if the event was registered with ``retval=True``,
1043
the given value, or a new effective value, should be returned.