~ubuntu-branches/debian/jessie/sqlalchemy/jessie

« back to all changes in this revision

Viewing changes to lib/sqlalchemy/orm/events.py

  • Committer: Bazaar Package Importer
  • Author(s): Piotr Ożarowski
  • Date: 2011-08-01 23:18:16 UTC
  • mfrom: (1.4.15 upstream) (16.1.14 experimental)
  • Revision ID: james.westby@ubuntu.com-20110801231816-6lx797pi3q1fpqst
Tags: 0.7.2-1
* New upstream release
* Bump minimum required python-mako version to 0.4.1 (closes: 635898)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# orm/events.py
 
2
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
 
3
#
 
4
# This module is part of SQLAlchemy and is released under
 
5
# the MIT License: http://www.opensource.org/licenses/mit-license.php
 
6
 
 
7
"""ORM event interfaces.
 
8
 
 
9
"""
 
10
from sqlalchemy import event, exc
 
11
import inspect
 
12
 
 
13
class InstrumentationEvents(event.Events):
 
14
    """Events related to class instrumentation events.
 
15
 
 
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.
 
22
 
 
23
    """
 
24
 
 
25
    @classmethod
 
26
    def _accept_with(cls, target):
 
27
        from sqlalchemy.orm.instrumentation import instrumentation_registry
 
28
 
 
29
        if isinstance(target, type):
 
30
            return instrumentation_registry
 
31
        else:
 
32
            return None
 
33
 
 
34
    @classmethod
 
35
    def _listen(cls, target, identifier, fn, propagate=False):
 
36
        event.Events._listen(target, identifier, fn, propagate=propagate)
 
37
 
 
38
    @classmethod
 
39
    def _remove(cls, identifier, target, fn):
 
40
        raise NotImplementedError("Removal of instrumentation events not yet implemented")
 
41
 
 
42
    def class_instrument(self, cls):
 
43
        """Called after the given class is instrumented.
 
44
 
 
45
        To get at the :class:`.ClassManager`, use
 
46
        :func:`.manager_of_class`.
 
47
 
 
48
        """
 
49
 
 
50
    def class_uninstrument(self, cls):
 
51
        """Called before the given class is uninstrumented.
 
52
 
 
53
        To get at the :class:`.ClassManager`, use
 
54
        :func:`.manager_of_class`.
 
55
 
 
56
        """
 
57
 
 
58
 
 
59
    def attribute_instrument(self, cls, key, inst):
 
60
        """Called when an attribute is instrumented."""
 
61
 
 
62
class InstanceEvents(event.Events):
 
63
    """Define events specific to object lifecycle.
 
64
 
 
65
    e.g.::
 
66
 
 
67
        from sqlalchemy import event
 
68
 
 
69
        def my_load_listener(target, context):
 
70
            print "on load!"
 
71
 
 
72
        event.listen(SomeMappedClass, 'load', my_load_listener)
 
73
 
 
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::
 
79
 
 
80
        from sqlalchemy.orm import mapper
 
81
 
 
82
        def some_listener(target, context):
 
83
            log.debug("Instance %s being loaded" % target)
 
84
 
 
85
        # attach to all mappers
 
86
        event.listen(mapper, 'load', some_listener)
 
87
 
 
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.
 
91
 
 
92
    When using :class:`.InstanceEvents`, several modifiers are
 
93
    available to the :func:`.event.listen` function.
 
94
 
 
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.
 
102
 
 
103
    """
 
104
    @classmethod
 
105
    def _accept_with(cls, target):
 
106
        from sqlalchemy.orm.instrumentation import ClassManager, manager_of_class
 
107
        from sqlalchemy.orm import Mapper, mapper
 
108
 
 
109
        if isinstance(target, ClassManager):
 
110
            return target
 
111
        elif isinstance(target, Mapper):
 
112
            return target.class_manager
 
113
        elif target is mapper:
 
114
            return ClassManager
 
115
        elif isinstance(target, type):
 
116
            if issubclass(target, Mapper):
 
117
                return ClassManager
 
118
            else:
 
119
                manager = manager_of_class(target)
 
120
                if manager:
 
121
                    return manager
 
122
        return None
 
123
 
 
124
    @classmethod
 
125
    def _listen(cls, target, identifier, fn, raw=False, propagate=False):
 
126
        if not raw:
 
127
            orig_fn = fn
 
128
            def wrap(state, *arg, **kw):
 
129
                return orig_fn(state.obj(), *arg, **kw)
 
130
            fn = wrap
 
131
 
 
132
        event.Events._listen(target, identifier, fn, propagate=propagate)
 
133
        if propagate:
 
134
            for mgr in target.subclass_managers(True):
 
135
                event.Events._listen(mgr, identifier, fn, True)
 
136
 
 
137
    @classmethod
 
138
    def _remove(cls, identifier, target, fn):
 
139
        raise NotImplementedError("Removal of instance events not yet implemented")
 
140
 
 
141
    def first_init(self, manager, cls):
 
142
        """Called when the first instance of a particular mapping is called.
 
143
 
 
144
        """
 
145
 
 
146
    def init(self, target, args, kwargs):
 
147
        """Receive an instance when it's constructor is called.
 
148
 
 
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
 
151
        database.
 
152
 
 
153
        """
 
154
 
 
155
    def init_failure(self, target, args, kwargs):
 
156
        """Receive an instance when it's constructor has been called, 
 
157
        and raised an exception.
 
158
 
 
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
 
161
        database.
 
162
 
 
163
        """
 
164
 
 
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
 
168
        occurred.
 
169
 
 
170
        This typically occurs when the instance is created based on
 
171
        incoming result rows, and is only called once for that
 
172
        instance's lifetime.
 
173
 
 
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.
 
178
 
 
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`.
 
187
 
 
188
        """
 
189
 
 
190
    def refresh(self, target, context, attrs):
 
191
        """Receive an object instance after one or more attributes have 
 
192
        been refreshed from a query.
 
193
 
 
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.
 
203
 
 
204
        """
 
205
 
 
206
    def expire(self, target, attrs):
 
207
        """Receive an object instance after its attributes or some subset
 
208
        have been expired.
 
209
 
 
210
        'keys' is a list of attribute names.  If None, the entire
 
211
        state was expired.
 
212
 
 
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 
 
219
         expired.
 
220
 
 
221
        """
 
222
 
 
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
 
226
        out of scope.
 
227
 
 
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.
 
232
 
 
233
        """
 
234
 
 
235
    def pickle(self, target, state_dict):
 
236
        """Receive an object instance when its associated state is
 
237
        being pickled.
 
238
 
 
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
 
245
         to be pickled.
 
246
         
 
247
        """
 
248
 
 
249
    def unpickle(self, target, state_dict):
 
250
        """Receive an object instance after it's associated state has
 
251
        been unpickled.
 
252
 
 
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.
 
260
        
 
261
        """
 
262
 
 
263
class MapperEvents(event.Events):
 
264
    """Define events specific to mappings.
 
265
 
 
266
    e.g.::
 
267
 
 
268
        from sqlalchemy import event
 
269
 
 
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)
 
276
 
 
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)
 
280
 
 
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::
 
286
 
 
287
        from sqlalchemy.orm import mapper
 
288
 
 
289
        def some_listener(mapper, connection, target):
 
290
            log.debug("Instance %s being inserted" % target)
 
291
 
 
292
        # attach to all mappers
 
293
        event.listen(mapper, 'before_insert', some_listener)
 
294
 
 
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.
 
307
 
 
308
    When using :class:`.MapperEvents`, several modifiers are
 
309
    available to the :func:`.event.listen` function.
 
310
 
 
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
 
322
       values are:
 
323
 
 
324
       * ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event
 
325
         processing normally.
 
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`.
 
331
 
 
332
    """
 
333
 
 
334
    @classmethod
 
335
    def _accept_with(cls, target):
 
336
        from sqlalchemy.orm import mapper, class_mapper, Mapper
 
337
        if target is mapper:
 
338
            return Mapper
 
339
        elif isinstance(target, type):
 
340
            if issubclass(target, Mapper):
 
341
                return target
 
342
            else:
 
343
                return class_mapper(target)
 
344
        else:
 
345
            return target
 
346
 
 
347
    @classmethod
 
348
    def _listen(cls, target, identifier, fn, 
 
349
                            raw=False, retval=False, propagate=False):
 
350
        from sqlalchemy.orm.interfaces import EXT_CONTINUE
 
351
 
 
352
        if not raw or not retval:
 
353
            if not raw:
 
354
                meth = getattr(cls, identifier)
 
355
                try:
 
356
                    target_index = inspect.getargspec(meth)[0].index('target') - 1
 
357
                except ValueError:
 
358
                    target_index = None
 
359
 
 
360
            wrapped_fn = fn
 
361
            def wrap(*arg, **kw):
 
362
                if not raw and target_index is not None:
 
363
                    arg = list(arg)
 
364
                    arg[target_index] = arg[target_index].obj()
 
365
                if not retval:
 
366
                    wrapped_fn(*arg, **kw)
 
367
                    return EXT_CONTINUE
 
368
                else:
 
369
                    return wrapped_fn(*arg, **kw)
 
370
            fn = wrap
 
371
 
 
372
        if propagate:
 
373
            for mapper in target.self_and_descendants:
 
374
                event.Events._listen(mapper, identifier, fn, propagate=True)
 
375
        else:
 
376
            event.Events._listen(target, identifier, fn)
 
377
 
 
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.
 
381
 
 
382
        This event is the earliest phase of mapper construction.
 
383
        Most attributes of the mapper are not yet initialized.
 
384
 
 
385
        This listener can generally only be applied to the :class:`.Mapper`
 
386
        class overall.
 
387
 
 
388
        :param mapper: the :class:`.Mapper` which is the target
 
389
         of this event.
 
390
        :param class\_: the mapped class.
 
391
 
 
392
        """
 
393
 
 
394
    def mapper_configured(self, mapper, class_):
 
395
        """Called when the mapper for the class is fully configured.
 
396
 
 
397
        This event is the latest phase of mapper construction.
 
398
        The mapper should be in its final state.
 
399
 
 
400
        :param mapper: the :class:`.Mapper` which is the target
 
401
         of this event.
 
402
        :param class\_: the mapped class.
 
403
 
 
404
        """
 
405
        # TODO: need coverage for this event
 
406
 
 
407
    def translate_row(self, mapper, context, row):
 
408
        """Perform pre-processing on the given result row and return a
 
409
        new row instance.
 
410
 
 
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.
 
419
 
 
420
        :param mapper: the :class:`.Mapper` which is the target
 
421
         of this event.
 
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.
 
431
 
 
432
 
 
433
        """
 
434
 
 
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.
 
438
 
 
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``.
 
442
 
 
443
        :param mapper: the :class:`.Mapper` which is the target
 
444
         of this event.
 
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
 
455
         should take place.
 
456
 
 
457
        """
 
458
 
 
459
    def append_result(self, mapper, context, row, target, 
 
460
                        result, **flags):
 
461
        """Receive an object instance before that instance is appended
 
462
        to a result list.
 
463
 
 
464
        This is a rarely used hook which can be used to alter
 
465
        the construction of a result list returned by :class:`.Query`.
 
466
 
 
467
        :param mapper: the :class:`.Mapper` which is the target
 
468
         of this event.
 
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
 
480
         appended.
 
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.
 
488
 
 
489
        """
 
490
 
 
491
 
 
492
    def populate_instance(self, mapper, context, row, 
 
493
                            target, **flags):
 
494
        """Receive an instance before that instance has
 
495
        its attributes populated.
 
496
 
 
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.
 
502
 
 
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`.
 
506
 
 
507
        :param mapper: the :class:`.Mapper` which is the target
 
508
         of this event.
 
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.
 
523
 
 
524
        """
 
525
 
 
526
    def before_insert(self, mapper, connection, target):
 
527
        """Receive an object instance before an INSERT statement
 
528
        is emitted corresponding to that instance.
 
529
 
 
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 
 
533
        connection.
 
534
 
 
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
 
542
        steps.
 
543
 
 
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`.
 
551
 
 
552
        :param mapper: the :class:`.Mapper` which is the target
 
553
         of this event.
 
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.
 
563
 
 
564
        """
 
565
 
 
566
    def after_insert(self, mapper, connection, target):
 
567
        """Receive an object instance after an INSERT statement
 
568
        is emitted corresponding to that instance.
 
569
 
 
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 
 
573
        connection.
 
574
 
 
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.
 
583
 
 
584
        :param mapper: the :class:`.Mapper` which is the target
 
585
         of this event.
 
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.
 
595
 
 
596
        """
 
597
 
 
598
    def before_update(self, mapper, connection, target):
 
599
        """Receive an object instance before an UPDATE statement
 
600
        is emitted corresponding to that instance.
 
601
 
 
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 
 
605
        connection.
 
606
 
 
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
 
619
        exist.
 
620
 
 
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)``.
 
625
 
 
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
 
633
        steps.
 
634
 
 
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`.
 
642
 
 
643
        :param mapper: the :class:`.Mapper` which is the target
 
644
         of this event.
 
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.
 
654
        """
 
655
 
 
656
    def after_update(self, mapper, connection, target):
 
657
        """Receive an object instance after an UPDATE statement
 
658
        is emitted corresponding to that instance.
 
659
 
 
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 
 
663
        connection.
 
664
 
 
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
 
676
        issued.
 
677
 
 
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)``.
 
682
 
 
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
 
690
        steps.
 
691
 
 
692
        :param mapper: the :class:`.Mapper` which is the target
 
693
         of this event.
 
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.
 
703
 
 
704
        """
 
705
 
 
706
    def before_delete(self, mapper, connection, target):
 
707
        """Receive an object instance before a DELETE statement
 
708
        is emitted corresponding to that instance.
 
709
 
 
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.
 
713
 
 
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. 
 
717
 
 
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`.
 
725
 
 
726
        :param mapper: the :class:`.Mapper` which is the target
 
727
         of this event.
 
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.
 
737
 
 
738
        """
 
739
 
 
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.
 
743
 
 
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.
 
747
 
 
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. 
 
751
 
 
752
        :param mapper: the :class:`.Mapper` which is the target
 
753
         of this event.
 
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.
 
763
 
 
764
        """
 
765
 
 
766
    @classmethod
 
767
    def _remove(cls, identifier, target, fn):
 
768
        raise NotImplementedError("Removal of mapper events not yet implemented")
 
769
 
 
770
class SessionEvents(event.Events):
 
771
    """Define events specific to :class:`.Session` lifecycle.
 
772
 
 
773
    e.g.::
 
774
 
 
775
        from sqlalchemy import event
 
776
        from sqlalchemy.orm import sessionmaker
 
777
 
 
778
        def my_before_commit(session):
 
779
            print "before commit!"
 
780
 
 
781
        Session = sessionmaker()
 
782
 
 
783
        event.listen(Session, "before_commit", my_before_commit)
 
784
 
 
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`.
 
788
 
 
789
    Additionally, it accepts the :class:`.Session` class which
 
790
    will apply listeners to all :class:`.Session` instances
 
791
    globally.
 
792
 
 
793
    """
 
794
 
 
795
    @classmethod
 
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):
 
808
                return Session
 
809
            elif issubclass(target, Session):
 
810
                return target
 
811
        elif isinstance(target, Session):
 
812
            return target
 
813
        else:
 
814
            return None
 
815
 
 
816
    @classmethod
 
817
    def _remove(cls, identifier, target, fn):
 
818
        raise NotImplementedError("Removal of session events not yet implemented")
 
819
 
 
820
    def before_commit(self, session):
 
821
        """Execute before commit is called.
 
822
 
 
823
        Note that this may not be per-flush if a longer running
 
824
        transaction is ongoing."""
 
825
 
 
826
    def after_commit(self, session):
 
827
        """Execute after a commit has occurred.
 
828
 
 
829
        Note that this may not be per-flush if a longer running
 
830
        transaction is ongoing."""
 
831
 
 
832
    def after_rollback(self, session):
 
833
        """Execute after a rollback has occurred.
 
834
 
 
835
        Note that this may not be per-flush if a longer running
 
836
        transaction is ongoing."""
 
837
 
 
838
    def before_flush( self, session, flush_context, instances):
 
839
        """Execute before flush process has started.
 
840
 
 
841
        `instances` is an optional list of objects which were passed to
 
842
        the ``flush()`` method. """
 
843
 
 
844
    def after_flush(self, session, flush_context):
 
845
        """Execute after flush has completed, but before commit has been
 
846
        called.
 
847
 
 
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."""
 
851
 
 
852
    def after_flush_postexec(self, session, flush_context):
 
853
        """Execute after flush has completed, and after the post-exec
 
854
        state occurs.
 
855
 
 
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. """
 
860
 
 
861
    def after_begin( self, session, transaction, connection):
 
862
        """Execute after a transaction is begun on a connection
 
863
 
 
864
        `transaction` is the SessionTransaction. This method is called
 
865
        after an engine level transaction is begun on a connection. """
 
866
 
 
867
    def after_attach(self, session, instance):
 
868
        """Execute after an instance is attached to a session.
 
869
 
 
870
        This is called after an add, delete or merge. """
 
871
 
 
872
    def after_bulk_update( self, session, query, query_context, result):
 
873
        """Execute after a bulk update operation to the session.
 
874
 
 
875
        This is called after a session.query(...).update()
 
876
 
 
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.
 
880
        """
 
881
 
 
882
    def after_bulk_delete( self, session, query, query_context, result):
 
883
        """Execute after a bulk delete operation to the session.
 
884
 
 
885
        This is called after a session.query(...).delete()
 
886
 
 
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.
 
890
        """
 
891
 
 
892
 
 
893
class AttributeEvents(event.Events):
 
894
    """Define events for object attributes.
 
895
 
 
896
    These are typically defined on the class-bound descriptor for the
 
897
    target class.
 
898
 
 
899
    e.g.::
 
900
 
 
901
        from sqlalchemy import event
 
902
 
 
903
        def my_append_listener(target, value, initiator):
 
904
            print "received append event for target: %s" % target
 
905
 
 
906
        event.listen(MyClass.collection, 'append', my_append_listener)
 
907
 
 
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`::
 
911
 
 
912
        def validate_phone(target, value, oldvalue, initiator):
 
913
            "Strip non-numeric characters from a phone number"
 
914
 
 
915
            return re.sub(r'(?![0-9])', '', value)
 
916
 
 
917
        # setup listener on UserContact.phone attribute, instructing
 
918
        # it to use the return value
 
919
        listen(UserContact.phone, 'set', validate_phone, retval=True)
 
920
 
 
921
    A validation function like the above can also raise an exception
 
922
    such as :class:`.ValueError` to halt the operation.
 
923
 
 
924
    Several modifiers are available to the :func:`~.event.listen` function.
 
925
 
 
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`.
 
932
 
 
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"
 
946
      or "append" event.
 
947
 
 
948
    """
 
949
 
 
950
    @classmethod
 
951
    def _accept_with(cls, target):
 
952
        from sqlalchemy.orm import interfaces
 
953
        # TODO: coverage
 
954
        if isinstance(target, interfaces.MapperProperty):
 
955
            return getattr(target.parent.class_, target.key)
 
956
        else:
 
957
            return target
 
958
 
 
959
    @classmethod
 
960
    def _listen(cls, target, identifier, fn, active_history=False, 
 
961
                                        raw=False, retval=False,
 
962
                                        propagate=False):
 
963
        if active_history:
 
964
            target.dispatch._active_history = True
 
965
 
 
966
        # TODO: for removal, need to package the identity
 
967
        # of the wrapper with the original function.
 
968
 
 
969
        if not raw or not retval:
 
970
            orig_fn = fn
 
971
            def wrap(target, value, *arg):
 
972
                if not raw:
 
973
                    target = target.obj()
 
974
                if not retval:
 
975
                    orig_fn(target, value, *arg)
 
976
                    return value
 
977
                else:
 
978
                    return orig_fn(target, value, *arg)
 
979
            fn = wrap
 
980
 
 
981
        event.Events._listen(target, identifier, fn, propagate)
 
982
 
 
983
        if propagate:
 
984
            from sqlalchemy.orm.instrumentation import manager_of_class
 
985
 
 
986
            manager = manager_of_class(target.class_)
 
987
 
 
988
            for mgr in manager.subclass_managers(True):
 
989
                event.Events._listen(mgr[target.key], identifier, fn, True)
 
990
 
 
991
    @classmethod
 
992
    def _remove(cls, identifier, target, fn):
 
993
        raise NotImplementedError("Removal of attribute events not yet implemented")
 
994
 
 
995
    def append(self, target, value, initiator):
 
996
        """Receive a collection append event.
 
997
 
 
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 
 
1004
          replaces it.
 
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.
 
1009
 
 
1010
        """
 
1011
 
 
1012
    def remove(self, target, value, initiator):
 
1013
        """Receive a collection remove event.
 
1014
 
 
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.
 
1022
        """
 
1023
 
 
1024
    def set(self, target, value, oldvalue, initiator):
 
1025
        """Receive a scalar set event.
 
1026
 
 
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 
 
1033
          replaces it.
 
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 
 
1039
          or expired.
 
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.
 
1044
 
 
1045
        """
 
1046