123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- # orm/deprecated_interfaces.py
- # Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
- # <see AUTHORS file>
- #
- # This module is part of SQLAlchemy and is released under
- # the MIT License: http://www.opensource.org/licenses/mit-license.php
- from .. import event, util
- from .interfaces import EXT_CONTINUE
- @util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
- class MapperExtension(object):
- """Base implementation for :class:`.Mapper` event hooks.
- .. note::
- :class:`.MapperExtension` is deprecated. Please
- refer to :func:`.event.listen` as well as
- :class:`.MapperEvents`.
- New extension classes subclass :class:`.MapperExtension` and are specified
- using the ``extension`` mapper() argument, which is a single
- :class:`.MapperExtension` or a list of such::
- from sqlalchemy.orm.interfaces import MapperExtension
- class MyExtension(MapperExtension):
- def before_insert(self, mapper, connection, instance):
- print "instance %s before insert !" % instance
- m = mapper(User, users_table, extension=MyExtension())
- A single mapper can maintain a chain of ``MapperExtension``
- objects. When a particular mapping event occurs, the
- corresponding method on each ``MapperExtension`` is invoked
- serially, and each method has the ability to halt the chain
- from proceeding further::
- m = mapper(User, users_table, extension=[ext1, ext2, ext3])
- Each ``MapperExtension`` method returns the symbol
- EXT_CONTINUE by default. This symbol generally means "move
- to the next ``MapperExtension`` for processing". For methods
- that return objects like translated rows or new object
- instances, EXT_CONTINUE means the result of the method
- should be ignored. In some cases it's required for a
- default mapper activity to be performed, such as adding a
- new instance to a result list.
- The symbol EXT_STOP has significance within a chain
- of ``MapperExtension`` objects that the chain will be stopped
- when this symbol is returned. Like EXT_CONTINUE, it also
- has additional significance in some cases that a default
- mapper activity will not be performed.
- """
- @classmethod
- def _adapt_instrument_class(cls, self, listener):
- cls._adapt_listener_methods(self, listener, ('instrument_class',))
- @classmethod
- def _adapt_listener(cls, self, listener):
- cls._adapt_listener_methods(
- self, listener,
- (
- 'init_instance',
- 'init_failed',
- 'reconstruct_instance',
- 'before_insert',
- 'after_insert',
- 'before_update',
- 'after_update',
- 'before_delete',
- 'after_delete'
- ))
- @classmethod
- def _adapt_listener_methods(cls, self, listener, methods):
- for meth in methods:
- me_meth = getattr(MapperExtension, meth)
- ls_meth = getattr(listener, meth)
- if not util.methods_equivalent(me_meth, ls_meth):
- if meth == 'reconstruct_instance':
- def go(ls_meth):
- def reconstruct(instance, ctx):
- ls_meth(self, instance)
- return reconstruct
- event.listen(self.class_manager, 'load',
- go(ls_meth), raw=False, propagate=True)
- elif meth == 'init_instance':
- def go(ls_meth):
- def init_instance(instance, args, kwargs):
- ls_meth(self, self.class_,
- self.class_manager.original_init,
- instance, args, kwargs)
- return init_instance
- event.listen(self.class_manager, 'init',
- go(ls_meth), raw=False, propagate=True)
- elif meth == 'init_failed':
- def go(ls_meth):
- def init_failed(instance, args, kwargs):
- util.warn_exception(
- ls_meth, self, self.class_,
- self.class_manager.original_init,
- instance, args, kwargs)
- return init_failed
- event.listen(self.class_manager, 'init_failure',
- go(ls_meth), raw=False, propagate=True)
- else:
- event.listen(self, "%s" % meth, ls_meth,
- raw=False, retval=True, propagate=True)
- def instrument_class(self, mapper, class_):
- """Receive a class when the mapper is first constructed, and has
- applied instrumentation to the mapped class.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def init_instance(self, mapper, class_, oldinit, instance, args, kwargs):
- """Receive an instance when its constructor is called.
- This method is only called during a userland construction of
- an object. It is not called when an object is loaded from the
- database.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def init_failed(self, mapper, class_, oldinit, instance, args, kwargs):
- """Receive an instance when its constructor has been called,
- and raised an exception.
- This method is only called during a userland construction of
- an object. It is not called when an object is loaded from the
- database.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def reconstruct_instance(self, mapper, instance):
- """Receive an object instance after it has been created via
- ``__new__``, and after initial attribute population has
- occurred.
- This typically occurs when the instance is created based on
- incoming result rows, and is only called once for that
- instance's lifetime.
- Note that during a result-row load, this method is called upon
- the first row received for this instance. Note that some
- attributes and collections may or may not be loaded or even
- initialized, depending on what's present in the result rows.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def before_insert(self, mapper, connection, instance):
- """Receive an object instance before that instance is inserted
- into its table.
- This is a good place to set up primary key values and such
- that aren't handled otherwise.
- Column-based attributes can be modified within this method
- which will result in the new value being inserted. However
- *no* changes to the overall flush plan can be made, and
- manipulation of the ``Session`` will not have the desired effect.
- To manipulate the ``Session`` within an extension, use
- ``SessionExtension``.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def after_insert(self, mapper, connection, instance):
- """Receive an object instance after that instance is inserted.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def before_update(self, mapper, connection, instance):
- """Receive an object instance before that instance is updated.
- Note that this method is called for all instances that are marked as
- "dirty", even those which have no net changes to their column-based
- attributes. An object is marked as dirty when any of its column-based
- attributes have a "set attribute" operation called or when any of its
- collections are modified. If, at update time, no column-based
- attributes have any net changes, no UPDATE statement will be issued.
- This means that an instance being sent to before_update is *not* a
- guarantee that an UPDATE statement will be issued (although you can
- affect the outcome here).
- To detect if the column-based attributes on the object have net
- changes, and will therefore generate an UPDATE statement, use
- ``object_session(instance).is_modified(instance,
- include_collections=False)``.
- Column-based attributes can be modified within this method
- which will result in the new value being updated. However
- *no* changes to the overall flush plan can be made, and
- manipulation of the ``Session`` will not have the desired effect.
- To manipulate the ``Session`` within an extension, use
- ``SessionExtension``.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def after_update(self, mapper, connection, instance):
- """Receive an object instance after that instance is updated.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def before_delete(self, mapper, connection, instance):
- """Receive an object instance before that instance is deleted.
- Note that *no* changes to the overall flush plan can be made
- here; and manipulation of the ``Session`` will not have the
- desired effect. To manipulate the ``Session`` within an
- extension, use ``SessionExtension``.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- def after_delete(self, mapper, connection, instance):
- """Receive an object instance after that instance is deleted.
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
- """
- return EXT_CONTINUE
- @util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
- class SessionExtension(object):
- """Base implementation for :class:`.Session` event hooks.
- .. note::
- :class:`.SessionExtension` is deprecated. Please
- refer to :func:`.event.listen` as well as
- :class:`.SessionEvents`.
- Subclasses may be installed into a :class:`.Session` (or
- :class:`.sessionmaker`) using the ``extension`` keyword
- argument::
- from sqlalchemy.orm.interfaces import SessionExtension
- class MySessionExtension(SessionExtension):
- def before_commit(self, session):
- print "before commit!"
- Session = sessionmaker(extension=MySessionExtension())
- The same :class:`.SessionExtension` instance can be used
- with any number of sessions.
- """
- @classmethod
- def _adapt_listener(cls, self, listener):
- for meth in [
- 'before_commit',
- 'after_commit',
- 'after_rollback',
- 'before_flush',
- 'after_flush',
- 'after_flush_postexec',
- 'after_begin',
- 'after_attach',
- 'after_bulk_update',
- 'after_bulk_delete',
- ]:
- me_meth = getattr(SessionExtension, meth)
- ls_meth = getattr(listener, meth)
- if not util.methods_equivalent(me_meth, ls_meth):
- event.listen(self, meth, getattr(listener, meth))
- def before_commit(self, session):
- """Execute right before commit is called.
- Note that this may not be per-flush if a longer running
- transaction is ongoing."""
- def after_commit(self, session):
- """Execute after a commit has occurred.
- Note that this may not be per-flush if a longer running
- transaction is ongoing."""
- def after_rollback(self, session):
- """Execute after a rollback has occurred.
- Note that this may not be per-flush if a longer running
- transaction is ongoing."""
- def before_flush(self, session, flush_context, instances):
- """Execute before flush process has started.
- `instances` is an optional list of objects which were passed to
- the ``flush()`` method. """
- def after_flush(self, session, flush_context):
- """Execute after flush has completed, but before commit has been
- called.
- Note that the session's state is still in pre-flush, i.e. 'new',
- 'dirty', and 'deleted' lists still show pre-flush state as well
- as the history settings on instance attributes."""
- def after_flush_postexec(self, session, flush_context):
- """Execute after flush has completed, and after the post-exec
- state occurs.
- This will be when the 'new', 'dirty', and 'deleted' lists are in
- their final state. An actual commit() may or may not have
- occurred, depending on whether or not the flush started its own
- transaction or participated in a larger transaction. """
- def after_begin(self, session, transaction, connection):
- """Execute after a transaction is begun on a connection
- `transaction` is the SessionTransaction. This method is called
- after an engine level transaction is begun on a connection. """
- def after_attach(self, session, instance):
- """Execute after an instance is attached to a session.
- This is called after an add, delete or merge. """
- def after_bulk_update(self, session, query, query_context, result):
- """Execute after a bulk update operation to the session.
- This is called after a session.query(...).update()
- `query` is the query object that this update operation was
- called on. `query_context` was the query context object.
- `result` is the result object returned from the bulk operation.
- """
- def after_bulk_delete(self, session, query, query_context, result):
- """Execute after a bulk delete operation to the session.
- This is called after a session.query(...).delete()
- `query` is the query object that this delete operation was
- called on. `query_context` was the query context object.
- `result` is the result object returned from the bulk operation.
- """
- @util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
- class AttributeExtension(object):
- """Base implementation for :class:`.AttributeImpl` event hooks, events
- that fire upon attribute mutations in user code.
- .. note::
- :class:`.AttributeExtension` is deprecated. Please
- refer to :func:`.event.listen` as well as
- :class:`.AttributeEvents`.
- :class:`.AttributeExtension` is used to listen for set,
- remove, and append events on individual mapped attributes.
- It is established on an individual mapped attribute using
- the `extension` argument, available on
- :func:`.column_property`, :func:`.relationship`, and
- others::
- from sqlalchemy.orm.interfaces import AttributeExtension
- from sqlalchemy.orm import mapper, relationship, column_property
- class MyAttrExt(AttributeExtension):
- def append(self, state, value, initiator):
- print "append event !"
- return value
- def set(self, state, value, oldvalue, initiator):
- print "set event !"
- return value
- mapper(SomeClass, sometable, properties={
- 'foo':column_property(sometable.c.foo, extension=MyAttrExt()),
- 'bar':relationship(Bar, extension=MyAttrExt())
- })
- Note that the :class:`.AttributeExtension` methods
- :meth:`~.AttributeExtension.append` and
- :meth:`~.AttributeExtension.set` need to return the
- ``value`` parameter. The returned value is used as the
- effective value, and allows the extension to change what is
- ultimately persisted.
- AttributeExtension is assembled within the descriptors associated
- with a mapped class.
- """
- active_history = True
- """indicates that the set() method would like to receive the 'old' value,
- even if it means firing lazy callables.
- Note that ``active_history`` can also be set directly via
- :func:`.column_property` and :func:`.relationship`.
- """
- @classmethod
- def _adapt_listener(cls, self, listener):
- event.listen(self, 'append', listener.append,
- active_history=listener.active_history,
- raw=True, retval=True)
- event.listen(self, 'remove', listener.remove,
- active_history=listener.active_history,
- raw=True, retval=True)
- event.listen(self, 'set', listener.set,
- active_history=listener.active_history,
- raw=True, retval=True)
- def append(self, state, value, initiator):
- """Receive a collection append event.
- The returned value will be used as the actual value to be
- appended.
- """
- return value
- def remove(self, state, value, initiator):
- """Receive a remove event.
- No return value is defined.
- """
- pass
- def set(self, state, value, oldvalue, initiator):
- """Receive a set event.
- The returned value will be used as the actual value to be
- set.
- """
- return value
|