Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/sqlalchemy/orm/deprecated_interfaces.py : 42%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# orm/deprecated_interfaces.py
2# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: http://www.opensource.org/licenses/mit-license.php
8from .interfaces import EXT_CONTINUE
9from .. import event
10from .. import util
13@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
14class MapperExtension(object):
15 """Base implementation for :class:`_orm.Mapper` event hooks.
17 .. deprecated:: 0.7
19 :class:`.MapperExtension` is deprecated and will be removed in a future
20 release. Please refer to :func:`.event.listen` in conjunction with
21 the :class:`.MapperEvents` listener interface.
23 New extension classes subclass :class:`.MapperExtension` and are specified
24 using the ``extension`` mapper() argument, which is a single
25 :class:`.MapperExtension` or a list of such::
27 from sqlalchemy.orm.interfaces import MapperExtension
29 class MyExtension(MapperExtension):
30 def before_insert(self, mapper, connection, instance):
31 print "instance %s before insert !" % instance
33 m = mapper(User, users_table, extension=MyExtension())
35 A single mapper can maintain a chain of ``MapperExtension``
36 objects. When a particular mapping event occurs, the
37 corresponding method on each ``MapperExtension`` is invoked
38 serially, and each method has the ability to halt the chain
39 from proceeding further::
41 m = mapper(User, users_table, extension=[ext1, ext2, ext3])
43 Each ``MapperExtension`` method returns the symbol
44 EXT_CONTINUE by default. This symbol generally means "move
45 to the next ``MapperExtension`` for processing". For methods
46 that return objects like translated rows or new object
47 instances, EXT_CONTINUE means the result of the method
48 should be ignored. In some cases it's required for a
49 default mapper activity to be performed, such as adding a
50 new instance to a result list.
52 The symbol EXT_STOP has significance within a chain
53 of ``MapperExtension`` objects that the chain will be stopped
54 when this symbol is returned. Like EXT_CONTINUE, it also
55 has additional significance in some cases that a default
56 mapper activity will not be performed.
58 """
60 @classmethod
61 def _adapt_instrument_class(cls, self, listener):
62 cls._adapt_listener_methods(self, listener, ("instrument_class",))
64 @classmethod
65 def _adapt_listener(cls, self, listener):
66 cls._adapt_listener_methods(
67 self,
68 listener,
69 (
70 "init_instance",
71 "init_failed",
72 "reconstruct_instance",
73 "before_insert",
74 "after_insert",
75 "before_update",
76 "after_update",
77 "before_delete",
78 "after_delete",
79 ),
80 )
82 @classmethod
83 def _adapt_listener_methods(cls, self, listener, methods):
85 for meth in methods:
86 me_meth = getattr(MapperExtension, meth)
87 ls_meth = getattr(listener, meth)
89 if not util.methods_equivalent(me_meth, ls_meth):
90 util.warn_deprecated(
91 "MapperExtension.%s is deprecated. The "
92 "MapperExtension class will be removed in a future "
93 "release. Please transition to the @event interface, "
94 "using @event.listens_for(mapped_class, '%s')."
95 % (meth, meth)
96 )
98 if meth == "reconstruct_instance":
100 def go(ls_meth):
101 def reconstruct(instance, ctx):
102 ls_meth(self, instance)
104 return reconstruct
106 event.listen(
107 self.class_manager,
108 "load",
109 go(ls_meth),
110 raw=False,
111 propagate=True,
112 )
113 elif meth == "init_instance":
115 def go(ls_meth):
116 def init_instance(instance, args, kwargs):
117 ls_meth(
118 self,
119 self.class_,
120 self.class_manager.original_init,
121 instance,
122 args,
123 kwargs,
124 )
126 return init_instance
128 event.listen(
129 self.class_manager,
130 "init",
131 go(ls_meth),
132 raw=False,
133 propagate=True,
134 )
135 elif meth == "init_failed":
137 def go(ls_meth):
138 def init_failed(instance, args, kwargs):
139 util.warn_exception(
140 ls_meth,
141 self,
142 self.class_,
143 self.class_manager.original_init,
144 instance,
145 args,
146 kwargs,
147 )
149 return init_failed
151 event.listen(
152 self.class_manager,
153 "init_failure",
154 go(ls_meth),
155 raw=False,
156 propagate=True,
157 )
158 else:
159 event.listen(
160 self,
161 "%s" % meth,
162 ls_meth,
163 raw=False,
164 retval=True,
165 propagate=True,
166 )
168 def instrument_class(self, mapper, class_):
169 """Receive a class when the mapper is first constructed, and has
170 applied instrumentation to the mapped class.
172 The return value is only significant within the ``MapperExtension``
173 chain; the parent mapper's behavior isn't modified by this method.
175 """
176 return EXT_CONTINUE
178 def init_instance(self, mapper, class_, oldinit, instance, args, kwargs):
179 """Receive an instance when its constructor is called.
181 This method is only called during a userland construction of
182 an object. It is not called when an object is loaded from the
183 database.
185 The return value is only significant within the ``MapperExtension``
186 chain; the parent mapper's behavior isn't modified by this method.
188 """
189 return EXT_CONTINUE
191 def init_failed(self, mapper, class_, oldinit, instance, args, kwargs):
192 """Receive an instance when its constructor has been called,
193 and raised an exception.
195 This method is only called during a userland construction of
196 an object. It is not called when an object is loaded from the
197 database.
199 The return value is only significant within the ``MapperExtension``
200 chain; the parent mapper's behavior isn't modified by this method.
202 """
203 return EXT_CONTINUE
205 def reconstruct_instance(self, mapper, instance):
206 """Receive an object instance after it has been created via
207 ``__new__``, and after initial attribute population has
208 occurred.
210 This typically occurs when the instance is created based on
211 incoming result rows, and is only called once for that
212 instance's lifetime.
214 Note that during a result-row load, this method is called upon
215 the first row received for this instance. Note that some
216 attributes and collections may or may not be loaded or even
217 initialized, depending on what's present in the result rows.
219 The return value is only significant within the ``MapperExtension``
220 chain; the parent mapper's behavior isn't modified by this method.
222 """
223 return EXT_CONTINUE
225 def before_insert(self, mapper, connection, instance):
226 """Receive an object instance before that instance is inserted
227 into its table.
229 This is a good place to set up primary key values and such
230 that aren't handled otherwise.
232 Column-based attributes can be modified within this method
233 which will result in the new value being inserted. However
234 *no* changes to the overall flush plan can be made, and
235 manipulation of the ``Session`` will not have the desired effect.
236 To manipulate the ``Session`` within an extension, use
237 ``SessionExtension``.
239 The return value is only significant within the ``MapperExtension``
240 chain; the parent mapper's behavior isn't modified by this method.
242 """
244 return EXT_CONTINUE
246 def after_insert(self, mapper, connection, instance):
247 """Receive an object instance after that instance is inserted.
249 The return value is only significant within the ``MapperExtension``
250 chain; the parent mapper's behavior isn't modified by this method.
252 """
254 return EXT_CONTINUE
256 def before_update(self, mapper, connection, instance):
257 """Receive an object instance before that instance is updated.
259 Note that this method is called for all instances that are marked as
260 "dirty", even those which have no net changes to their column-based
261 attributes. An object is marked as dirty when any of its column-based
262 attributes have a "set attribute" operation called or when any of its
263 collections are modified. If, at update time, no column-based
264 attributes have any net changes, no UPDATE statement will be issued.
265 This means that an instance being sent to before_update is *not* a
266 guarantee that an UPDATE statement will be issued (although you can
267 affect the outcome here).
269 To detect if the column-based attributes on the object have net
270 changes, and will therefore generate an UPDATE statement, use
271 ``object_session(instance).is_modified(instance,
272 include_collections=False)``.
274 Column-based attributes can be modified within this method
275 which will result in the new value being updated. However
276 *no* changes to the overall flush plan can be made, and
277 manipulation of the ``Session`` will not have the desired effect.
278 To manipulate the ``Session`` within an extension, use
279 ``SessionExtension``.
281 The return value is only significant within the ``MapperExtension``
282 chain; the parent mapper's behavior isn't modified by this method.
284 """
286 return EXT_CONTINUE
288 def after_update(self, mapper, connection, instance):
289 """Receive an object instance after that instance is updated.
291 The return value is only significant within the ``MapperExtension``
292 chain; the parent mapper's behavior isn't modified by this method.
294 """
296 return EXT_CONTINUE
298 def before_delete(self, mapper, connection, instance):
299 """Receive an object instance before that instance is deleted.
301 Note that *no* changes to the overall flush plan can be made
302 here; and manipulation of the ``Session`` will not have the
303 desired effect. To manipulate the ``Session`` within an
304 extension, use ``SessionExtension``.
306 The return value is only significant within the ``MapperExtension``
307 chain; the parent mapper's behavior isn't modified by this method.
309 """
311 return EXT_CONTINUE
313 def after_delete(self, mapper, connection, instance):
314 """Receive an object instance after that instance is deleted.
316 The return value is only significant within the ``MapperExtension``
317 chain; the parent mapper's behavior isn't modified by this method.
319 """
321 return EXT_CONTINUE
324@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
325class SessionExtension(object):
327 """Base implementation for :class:`.Session` event hooks.
329 .. deprecated:: 0.7
331 :class:`.SessionExtension` is deprecated and will be removed in a future
332 release. Please refer to :func:`.event.listen` in conjunction with
333 the :class:`.SessionEvents` listener interface.
335 Subclasses may be installed into a :class:`.Session` (or
336 :class:`.sessionmaker`) using the ``extension`` keyword
337 argument::
339 from sqlalchemy.orm.interfaces import SessionExtension
341 class MySessionExtension(SessionExtension):
342 def before_commit(self, session):
343 print "before commit!"
345 Session = sessionmaker(extension=MySessionExtension())
347 The same :class:`.SessionExtension` instance can be used
348 with any number of sessions.
350 """
352 @classmethod
353 def _adapt_listener(cls, self, listener):
354 for meth in [
355 "before_commit",
356 "after_commit",
357 "after_rollback",
358 "before_flush",
359 "after_flush",
360 "after_flush_postexec",
361 "after_begin",
362 "after_attach",
363 "after_bulk_update",
364 "after_bulk_delete",
365 ]:
366 me_meth = getattr(SessionExtension, meth)
367 ls_meth = getattr(listener, meth)
369 if not util.methods_equivalent(me_meth, ls_meth):
370 util.warn_deprecated(
371 "SessionExtension.%s is deprecated. The "
372 "SessionExtension class will be removed in a future "
373 "release. Please transition to the @event interface, "
374 "using @event.listens_for(Session, '%s')." % (meth, meth)
375 )
377 event.listen(self, meth, getattr(listener, meth))
379 def before_commit(self, session):
380 """Execute right before commit is called.
382 Note that this may not be per-flush if a longer running
383 transaction is ongoing."""
385 def after_commit(self, session):
386 """Execute after a commit has occurred.
388 Note that this may not be per-flush if a longer running
389 transaction is ongoing."""
391 def after_rollback(self, session):
392 """Execute after a rollback has occurred.
394 Note that this may not be per-flush if a longer running
395 transaction is ongoing."""
397 def before_flush(self, session, flush_context, instances):
398 """Execute before flush process has started.
400 `instances` is an optional list of objects which were passed to
401 the ``flush()`` method. """
403 def after_flush(self, session, flush_context):
404 """Execute after flush has completed, but before commit has been
405 called.
407 Note that the session's state is still in pre-flush, i.e. 'new',
408 'dirty', and 'deleted' lists still show pre-flush state as well
409 as the history settings on instance attributes."""
411 def after_flush_postexec(self, session, flush_context):
412 """Execute after flush has completed, and after the post-exec
413 state occurs.
415 This will be when the 'new', 'dirty', and 'deleted' lists are in
416 their final state. An actual commit() may or may not have
417 occurred, depending on whether or not the flush started its own
418 transaction or participated in a larger transaction. """
420 def after_begin(self, session, transaction, connection):
421 """Execute after a transaction is begun on a connection
423 `transaction` is the SessionTransaction. This method is called
424 after an engine level transaction is begun on a connection. """
426 def after_attach(self, session, instance):
427 """Execute after an instance is attached to a session.
429 This is called after an add, delete or merge. """
431 def after_bulk_update(self, session, query, query_context, result):
432 """Execute after a bulk update operation to the session.
434 This is called after a session.query(...).update()
436 `query` is the query object that this update operation was
437 called on. `query_context` was the query context object.
438 `result` is the result object returned from the bulk operation.
439 """
441 def after_bulk_delete(self, session, query, query_context, result):
442 """Execute after a bulk delete operation to the session.
444 This is called after a session.query(...).delete()
446 `query` is the query object that this delete operation was
447 called on. `query_context` was the query context object.
448 `result` is the result object returned from the bulk operation.
449 """
452@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
453class AttributeExtension(object):
454 """Base implementation for :class:`.AttributeImpl` event hooks, events
455 that fire upon attribute mutations in user code.
457 .. deprecated:: 0.7
459 :class:`.AttributeExtension` is deprecated and will be removed in a
460 future release. Please refer to :func:`.event.listen` in conjunction
461 with the :class:`.AttributeEvents` listener interface.
463 :class:`.AttributeExtension` is used to listen for set,
464 remove, and append events on individual mapped attributes.
465 It is established on an individual mapped attribute using
466 the `extension` argument, available on
467 :func:`.column_property`, :func:`_orm.relationship`, and
468 others::
470 from sqlalchemy.orm.interfaces import AttributeExtension
471 from sqlalchemy.orm import mapper, relationship, column_property
473 class MyAttrExt(AttributeExtension):
474 def append(self, state, value, initiator):
475 print "append event !"
476 return value
478 def set(self, state, value, oldvalue, initiator):
479 print "set event !"
480 return value
482 mapper(SomeClass, sometable, properties={
483 'foo':column_property(sometable.c.foo, extension=MyAttrExt()),
484 'bar':relationship(Bar, extension=MyAttrExt())
485 })
487 Note that the :class:`.AttributeExtension` methods
488 :meth:`~.AttributeExtension.append` and
489 :meth:`~.AttributeExtension.set` need to return the
490 ``value`` parameter. The returned value is used as the
491 effective value, and allows the extension to change what is
492 ultimately persisted.
494 AttributeExtension is assembled within the descriptors associated
495 with a mapped class.
497 """
499 active_history = True
500 """indicates that the set() method would like to receive the 'old' value,
501 even if it means firing lazy callables.
503 Note that ``active_history`` can also be set directly via
504 :func:`.column_property` and :func:`_orm.relationship`.
506 """
508 @classmethod
509 def _adapt_listener(cls, self, listener):
510 for meth in ["append", "remove", "set"]:
511 me_meth = getattr(AttributeExtension, meth)
512 ls_meth = getattr(listener, meth)
514 if not util.methods_equivalent(me_meth, ls_meth):
515 util.warn_deprecated(
516 "AttributeExtension.%s is deprecated. The "
517 "AttributeExtension class will be removed in a future "
518 "release. Please transition to the @event interface, "
519 "using @event.listens_for(Class.attribute, '%s')."
520 % (meth, meth)
521 )
523 event.listen(
524 self,
525 "append",
526 listener.append,
527 active_history=listener.active_history,
528 raw=True,
529 retval=True,
530 )
531 event.listen(
532 self,
533 "remove",
534 listener.remove,
535 active_history=listener.active_history,
536 raw=True,
537 retval=True,
538 )
539 event.listen(
540 self,
541 "set",
542 listener.set,
543 active_history=listener.active_history,
544 raw=True,
545 retval=True,
546 )
548 def append(self, state, value, initiator):
549 """Receive a collection append event.
551 The returned value will be used as the actual value to be
552 appended.
554 """
555 return value
557 def remove(self, state, value, initiator):
558 """Receive a remove event.
560 No return value is defined.
562 """
563 pass
565 def set(self, state, value, oldvalue, initiator):
566 """Receive a set event.
568 The returned value will be used as the actual value to be
569 set.
571 """
572 return value