123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187 |
- # orm/events.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
- """ORM event interfaces.
- """
- from .. import event, exc, util
- from .base import _mapper_or_none
- import inspect
- import weakref
- from . import interfaces
- from . import mapperlib, instrumentation
- from .session import Session, sessionmaker
- from .scoping import scoped_session
- from .attributes import QueryableAttribute
- from .query import Query
- from sqlalchemy.util.compat import inspect_getargspec
- class InstrumentationEvents(event.Events):
- """Events related to class instrumentation events.
- The listeners here support being established against
- any new style class, that is any object that is a subclass
- of 'type'. Events will then be fired off for events
- against that class. If the "propagate=True" flag is passed
- to event.listen(), the event will fire off for subclasses
- of that class as well.
- The Python ``type`` builtin is also accepted as a target,
- which when used has the effect of events being emitted
- for all classes.
- Note the "propagate" flag here is defaulted to ``True``,
- unlike the other class level events where it defaults
- to ``False``. This means that new subclasses will also
- be the subject of these events, when a listener
- is established on a superclass.
- .. versionchanged:: 0.8 - events here will emit based
- on comparing the incoming class to the type of class
- passed to :func:`.event.listen`. Previously, the
- event would fire for any class unconditionally regardless
- of what class was sent for listening, despite
- documentation which stated the contrary.
- """
- _target_class_doc = "SomeBaseClass"
- _dispatch_target = instrumentation.InstrumentationFactory
- @classmethod
- def _accept_with(cls, target):
- if isinstance(target, type):
- return _InstrumentationEventsHold(target)
- else:
- return None
- @classmethod
- def _listen(cls, event_key, propagate=True, **kw):
- target, identifier, fn = \
- event_key.dispatch_target, event_key.identifier, \
- event_key._listen_fn
- def listen(target_cls, *arg):
- listen_cls = target()
- if propagate and issubclass(target_cls, listen_cls):
- return fn(target_cls, *arg)
- elif not propagate and target_cls is listen_cls:
- return fn(target_cls, *arg)
- def remove(ref):
- key = event.registry._EventKey(
- None, identifier, listen,
- instrumentation._instrumentation_factory)
- getattr(instrumentation._instrumentation_factory.dispatch,
- identifier).remove(key)
- target = weakref.ref(target.class_, remove)
- event_key.\
- with_dispatch_target(instrumentation._instrumentation_factory).\
- with_wrapper(listen).base_listen(**kw)
- @classmethod
- def _clear(cls):
- super(InstrumentationEvents, cls)._clear()
- instrumentation._instrumentation_factory.dispatch._clear()
- def class_instrument(self, cls):
- """Called after the given class is instrumented.
- To get at the :class:`.ClassManager`, use
- :func:`.manager_of_class`.
- """
- def class_uninstrument(self, cls):
- """Called before the given class is uninstrumented.
- To get at the :class:`.ClassManager`, use
- :func:`.manager_of_class`.
- """
- def attribute_instrument(self, cls, key, inst):
- """Called when an attribute is instrumented."""
- class _InstrumentationEventsHold(object):
- """temporary marker object used to transfer from _accept_with() to
- _listen() on the InstrumentationEvents class.
- """
- def __init__(self, class_):
- self.class_ = class_
- dispatch = event.dispatcher(InstrumentationEvents)
- class InstanceEvents(event.Events):
- """Define events specific to object lifecycle.
- e.g.::
- from sqlalchemy import event
- def my_load_listener(target, context):
- print "on load!"
- event.listen(SomeClass, 'load', my_load_listener)
- Available targets include:
- * mapped classes
- * unmapped superclasses of mapped or to-be-mapped classes
- (using the ``propagate=True`` flag)
- * :class:`.Mapper` objects
- * the :class:`.Mapper` class itself and the :func:`.mapper`
- function indicate listening for all mappers.
- .. versionchanged:: 0.8.0 instance events can be associated with
- unmapped superclasses of mapped classes.
- Instance events are closely related to mapper events, but
- are more specific to the instance and its instrumentation,
- rather than its system of persistence.
- When using :class:`.InstanceEvents`, several modifiers are
- available to the :func:`.event.listen` function.
- :param propagate=False: When True, the event listener should
- be applied to all inheriting classes as well as the
- class which is the target of this listener.
- :param raw=False: When True, the "target" argument passed
- to applicable event listener functions will be the
- instance's :class:`.InstanceState` management
- object, rather than the mapped instance itself.
- """
- _target_class_doc = "SomeClass"
- _dispatch_target = instrumentation.ClassManager
- @classmethod
- def _new_classmanager_instance(cls, class_, classmanager):
- _InstanceEventsHold.populate(class_, classmanager)
- @classmethod
- @util.dependencies("sqlalchemy.orm")
- def _accept_with(cls, orm, target):
- if isinstance(target, instrumentation.ClassManager):
- return target
- elif isinstance(target, mapperlib.Mapper):
- return target.class_manager
- elif target is orm.mapper:
- return instrumentation.ClassManager
- elif isinstance(target, type):
- if issubclass(target, mapperlib.Mapper):
- return instrumentation.ClassManager
- else:
- manager = instrumentation.manager_of_class(target)
- if manager:
- return manager
- else:
- return _InstanceEventsHold(target)
- return None
- @classmethod
- def _listen(cls, event_key, raw=False, propagate=False, **kw):
- target, identifier, fn = \
- event_key.dispatch_target, event_key.identifier, \
- event_key._listen_fn
- if not raw:
- def wrap(state, *arg, **kw):
- return fn(state.obj(), *arg, **kw)
- event_key = event_key.with_wrapper(wrap)
- event_key.base_listen(propagate=propagate, **kw)
- if propagate:
- for mgr in target.subclass_managers(True):
- event_key.with_dispatch_target(mgr).base_listen(
- propagate=True)
- @classmethod
- def _clear(cls):
- super(InstanceEvents, cls)._clear()
- _InstanceEventsHold._clear()
- def first_init(self, manager, cls):
- """Called when the first instance of a particular mapping is called.
- This event is called when the ``__init__`` method of a class
- is called the first time for that particular class. The event
- invokes before ``__init__`` actually proceeds as well as before
- the :meth:`.InstanceEvents.init` event is invoked.
- """
- def init(self, target, args, kwargs):
- """Receive an instance when its constructor is called.
- This method is only called during a userland construction of
- an object, in conjunction with the object's constructor, e.g.
- its ``__init__`` method. It is not called when an object is
- loaded from the database; see the :meth:`.InstanceEvents.load`
- event in order to intercept a database load.
- The event is called before the actual ``__init__`` constructor
- of the object is called. The ``kwargs`` dictionary may be
- modified in-place in order to affect what is passed to
- ``__init__``.
- :param target: the mapped instance. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :param args: positional arguments passed to the ``__init__`` method.
- This is passed as a tuple and is currently immutable.
- :param kwargs: keyword arguments passed to the ``__init__`` method.
- This structure *can* be altered in place.
- .. seealso::
- :meth:`.InstanceEvents.init_failure`
- :meth:`.InstanceEvents.load`
- """
- def init_failure(self, target, 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, in conjunction with the object's constructor, e.g.
- its ``__init__`` method. It is not called when an object is loaded
- from the database.
- The event is invoked after an exception raised by the ``__init__``
- method is caught. After the event
- is invoked, the original exception is re-raised outwards, so that
- the construction of the object still raises an exception. The
- actual exception and stack trace raised should be present in
- ``sys.exc_info()``.
- :param target: the mapped instance. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :param args: positional arguments that were passed to the ``__init__``
- method.
- :param kwargs: keyword arguments that were passed to the ``__init__``
- method.
- .. seealso::
- :meth:`.InstanceEvents.init`
- :meth:`.InstanceEvents.load`
- """
- def load(self, target, context):
- """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.
- :param target: the mapped instance. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :param context: the :class:`.QueryContext` corresponding to the
- current :class:`.Query` in progress. This argument may be
- ``None`` if the load does not correspond to a :class:`.Query`,
- such as during :meth:`.Session.merge`.
- .. seealso::
- :meth:`.InstanceEvents.init`
- :meth:`.InstanceEvents.refresh`
- :meth:`.SessionEvents.loaded_as_persistent`
- """
- def refresh(self, target, context, attrs):
- """Receive an object instance after one or more attributes have
- been refreshed from a query.
- Contrast this to the :meth:`.InstanceEvents.load` method, which
- is invoked when the object is first loaded from a query.
- :param target: the mapped instance. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :param context: the :class:`.QueryContext` corresponding to the
- current :class:`.Query` in progress.
- :param attrs: sequence of attribute names which
- were populated, or None if all column-mapped, non-deferred
- attributes were populated.
- .. seealso::
- :meth:`.InstanceEvents.load`
- """
- def refresh_flush(self, target, flush_context, attrs):
- """Receive an object instance after one or more attributes have
- been refreshed within the persistence of the object.
- This event is the same as :meth:`.InstanceEvents.refresh` except
- it is invoked within the unit of work flush process, and the values
- here typically come from the process of handling an INSERT or
- UPDATE, such as via the RETURNING clause or from Python-side default
- values.
- .. versionadded:: 1.0.5
- :param target: the mapped instance. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :param flush_context: Internal :class:`.UOWTransaction` object
- which handles the details of the flush.
- :param attrs: sequence of attribute names which
- were populated.
- """
- def expire(self, target, attrs):
- """Receive an object instance after its attributes or some subset
- have been expired.
- 'keys' is a list of attribute names. If None, the entire
- state was expired.
- :param target: the mapped instance. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :param attrs: sequence of attribute
- names which were expired, or None if all attributes were
- expired.
- """
- def pickle(self, target, state_dict):
- """Receive an object instance when its associated state is
- being pickled.
- :param target: the mapped instance. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :param state_dict: the dictionary returned by
- :class:`.InstanceState.__getstate__`, containing the state
- to be pickled.
- """
- def unpickle(self, target, state_dict):
- """Receive an object instance after its associated state has
- been unpickled.
- :param target: the mapped instance. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :param state_dict: the dictionary sent to
- :class:`.InstanceState.__setstate__`, containing the state
- dictionary which was pickled.
- """
- class _EventsHold(event.RefCollection):
- """Hold onto listeners against unmapped, uninstrumented classes.
- Establish _listen() for that class' mapper/instrumentation when
- those objects are created for that class.
- """
- def __init__(self, class_):
- self.class_ = class_
- @classmethod
- def _clear(cls):
- cls.all_holds.clear()
- class HoldEvents(object):
- _dispatch_target = None
- @classmethod
- def _listen(cls, event_key, raw=False, propagate=False, **kw):
- target, identifier, fn = \
- event_key.dispatch_target, event_key.identifier, event_key.fn
- if target.class_ in target.all_holds:
- collection = target.all_holds[target.class_]
- else:
- collection = target.all_holds[target.class_] = {}
- event.registry._stored_in_collection(event_key, target)
- collection[event_key._key] = (event_key, raw, propagate)
- if propagate:
- stack = list(target.class_.__subclasses__())
- while stack:
- subclass = stack.pop(0)
- stack.extend(subclass.__subclasses__())
- subject = target.resolve(subclass)
- if subject is not None:
- # we are already going through __subclasses__()
- # so leave generic propagate flag False
- event_key.with_dispatch_target(subject).\
- listen(raw=raw, propagate=False, **kw)
- def remove(self, event_key):
- target, identifier, fn = \
- event_key.dispatch_target, event_key.identifier, event_key.fn
- if isinstance(target, _EventsHold):
- collection = target.all_holds[target.class_]
- del collection[event_key._key]
- @classmethod
- def populate(cls, class_, subject):
- for subclass in class_.__mro__:
- if subclass in cls.all_holds:
- collection = cls.all_holds[subclass]
- for event_key, raw, propagate in collection.values():
- if propagate or subclass is class_:
- # since we can't be sure in what order different
- # classes in a hierarchy are triggered with
- # populate(), we rely upon _EventsHold for all event
- # assignment, instead of using the generic propagate
- # flag.
- event_key.with_dispatch_target(subject).\
- listen(raw=raw, propagate=False)
- class _InstanceEventsHold(_EventsHold):
- all_holds = weakref.WeakKeyDictionary()
- def resolve(self, class_):
- return instrumentation.manager_of_class(class_)
- class HoldInstanceEvents(_EventsHold.HoldEvents, InstanceEvents):
- pass
- dispatch = event.dispatcher(HoldInstanceEvents)
- class MapperEvents(event.Events):
- """Define events specific to mappings.
- e.g.::
- from sqlalchemy import event
- def my_before_insert_listener(mapper, connection, target):
- # execute a stored procedure upon INSERT,
- # apply the value to the row to be inserted
- target.calculated_value = connection.scalar(
- "select my_special_function(%d)"
- % target.special_number)
- # associate the listener function with SomeClass,
- # to execute during the "before_insert" hook
- event.listen(
- SomeClass, 'before_insert', my_before_insert_listener)
- Available targets include:
- * mapped classes
- * unmapped superclasses of mapped or to-be-mapped classes
- (using the ``propagate=True`` flag)
- * :class:`.Mapper` objects
- * the :class:`.Mapper` class itself and the :func:`.mapper`
- function indicate listening for all mappers.
- .. versionchanged:: 0.8.0 mapper events can be associated with
- unmapped superclasses of mapped classes.
- Mapper events provide hooks into critical sections of the
- mapper, including those related to object instrumentation,
- object loading, and object persistence. In particular, the
- persistence methods :meth:`~.MapperEvents.before_insert`,
- and :meth:`~.MapperEvents.before_update` are popular
- places to augment the state being persisted - however, these
- methods operate with several significant restrictions. The
- user is encouraged to evaluate the
- :meth:`.SessionEvents.before_flush` and
- :meth:`.SessionEvents.after_flush` methods as more
- flexible and user-friendly hooks in which to apply
- additional database state during a flush.
- When using :class:`.MapperEvents`, several modifiers are
- available to the :func:`.event.listen` function.
- :param propagate=False: When True, the event listener should
- be applied to all inheriting mappers and/or the mappers of
- inheriting classes, as well as any
- mapper which is the target of this listener.
- :param raw=False: When True, the "target" argument passed
- to applicable event listener functions will be the
- instance's :class:`.InstanceState` management
- object, rather than the mapped instance itself.
- :param retval=False: when True, the user-defined event function
- must have a return value, the purpose of which is either to
- control subsequent event propagation, or to otherwise alter
- the operation in progress by the mapper. Possible return
- values are:
- * ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event
- processing normally.
- * ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent
- event handlers in the chain.
- * other values - the return value specified by specific listeners.
- """
- _target_class_doc = "SomeClass"
- _dispatch_target = mapperlib.Mapper
- @classmethod
- def _new_mapper_instance(cls, class_, mapper):
- _MapperEventsHold.populate(class_, mapper)
- @classmethod
- @util.dependencies("sqlalchemy.orm")
- def _accept_with(cls, orm, target):
- if target is orm.mapper:
- return mapperlib.Mapper
- elif isinstance(target, type):
- if issubclass(target, mapperlib.Mapper):
- return target
- else:
- mapper = _mapper_or_none(target)
- if mapper is not None:
- return mapper
- else:
- return _MapperEventsHold(target)
- else:
- return target
- @classmethod
- def _listen(
- cls, event_key, raw=False, retval=False, propagate=False, **kw):
- target, identifier, fn = \
- event_key.dispatch_target, event_key.identifier, \
- event_key._listen_fn
- if identifier in ("before_configured", "after_configured") and \
- target is not mapperlib.Mapper:
- util.warn(
- "'before_configured' and 'after_configured' ORM events "
- "only invoke with the mapper() function or Mapper class "
- "as the target.")
- if not raw or not retval:
- if not raw:
- meth = getattr(cls, identifier)
- try:
- target_index = \
- inspect_getargspec(meth)[0].index('target') - 1
- except ValueError:
- target_index = None
- def wrap(*arg, **kw):
- if not raw and target_index is not None:
- arg = list(arg)
- arg[target_index] = arg[target_index].obj()
- if not retval:
- fn(*arg, **kw)
- return interfaces.EXT_CONTINUE
- else:
- return fn(*arg, **kw)
- event_key = event_key.with_wrapper(wrap)
- if propagate:
- for mapper in target.self_and_descendants:
- event_key.with_dispatch_target(mapper).base_listen(
- propagate=True, **kw)
- else:
- event_key.base_listen(**kw)
- @classmethod
- def _clear(cls):
- super(MapperEvents, cls)._clear()
- _MapperEventsHold._clear()
- def instrument_class(self, mapper, class_):
- r"""Receive a class when the mapper is first constructed,
- before instrumentation is applied to the mapped class.
- This event is the earliest phase of mapper construction.
- Most attributes of the mapper are not yet initialized.
- This listener can either be applied to the :class:`.Mapper`
- class overall, or to any un-mapped class which serves as a base
- for classes that will be mapped (using the ``propagate=True`` flag)::
- Base = declarative_base()
- @event.listens_for(Base, "instrument_class", propagate=True)
- def on_new_class(mapper, cls_):
- " ... "
- :param mapper: the :class:`.Mapper` which is the target
- of this event.
- :param class\_: the mapped class.
- """
- def mapper_configured(self, mapper, class_):
- r"""Called when a specific mapper has completed its own configuration
- within the scope of the :func:`.configure_mappers` call.
- The :meth:`.MapperEvents.mapper_configured` event is invoked
- for each mapper that is encountered when the
- :func:`.orm.configure_mappers` function proceeds through the current
- list of not-yet-configured mappers.
- :func:`.orm.configure_mappers` is typically invoked
- automatically as mappings are first used, as well as each time
- new mappers have been made available and new mapper use is
- detected.
- When the event is called, the mapper should be in its final
- state, but **not including backrefs** that may be invoked from
- other mappers; they might still be pending within the
- configuration operation. Bidirectional relationships that
- are instead configured via the
- :paramref:`.orm.relationship.back_populates` argument
- *will* be fully available, since this style of relationship does not
- rely upon other possibly-not-configured mappers to know that they
- exist.
- For an event that is guaranteed to have **all** mappers ready
- to go including backrefs that are defined only on other
- mappings, use the :meth:`.MapperEvents.after_configured`
- event; this event invokes only after all known mappings have been
- fully configured.
- The :meth:`.MapperEvents.mapper_configured` event, unlike
- :meth:`.MapperEvents.before_configured` or
- :meth:`.MapperEvents.after_configured`,
- is called for each mapper/class individually, and the mapper is
- passed to the event itself. It also is called exactly once for
- a particular mapper. The event is therefore useful for
- configurational steps that benefit from being invoked just once
- on a specific mapper basis, which don't require that "backref"
- configurations are necessarily ready yet.
- :param mapper: the :class:`.Mapper` which is the target
- of this event.
- :param class\_: the mapped class.
- .. seealso::
- :meth:`.MapperEvents.before_configured`
- :meth:`.MapperEvents.after_configured`
- """
- # TODO: need coverage for this event
- def before_configured(self):
- """Called before a series of mappers have been configured.
- The :meth:`.MapperEvents.before_configured` event is invoked
- each time the :func:`.orm.configure_mappers` function is
- invoked, before the function has done any of its work.
- :func:`.orm.configure_mappers` is typically invoked
- automatically as mappings are first used, as well as each time
- new mappers have been made available and new mapper use is
- detected.
- This event can **only** be applied to the :class:`.Mapper` class
- or :func:`.mapper` function, and not to individual mappings or
- mapped classes. It is only invoked for all mappings as a whole::
- from sqlalchemy.orm import mapper
- @event.listens_for(mapper, "before_configured")
- def go():
- # ...
- Constrast this event to :meth:`.MapperEvents.after_configured`,
- which is invoked after the series of mappers has been configured,
- as well as :meth:`.MapperEvents.mapper_configured`, which is invoked
- on a per-mapper basis as each one is configured to the extent possible.
- Theoretically this event is called once per
- application, but is actually called any time new mappers
- are to be affected by a :func:`.orm.configure_mappers`
- call. If new mappings are constructed after existing ones have
- already been used, this event will likely be called again. To ensure
- that a particular event is only called once and no further, the
- ``once=True`` argument (new in 0.9.4) can be applied::
- from sqlalchemy.orm import mapper
- @event.listens_for(mapper, "before_configured", once=True)
- def go():
- # ...
- .. versionadded:: 0.9.3
- .. seealso::
- :meth:`.MapperEvents.mapper_configured`
- :meth:`.MapperEvents.after_configured`
- """
- def after_configured(self):
- """Called after a series of mappers have been configured.
- The :meth:`.MapperEvents.after_configured` event is invoked
- each time the :func:`.orm.configure_mappers` function is
- invoked, after the function has completed its work.
- :func:`.orm.configure_mappers` is typically invoked
- automatically as mappings are first used, as well as each time
- new mappers have been made available and new mapper use is
- detected.
- Contrast this event to the :meth:`.MapperEvents.mapper_configured`
- event, which is called on a per-mapper basis while the configuration
- operation proceeds; unlike that event, when this event is invoked,
- all cross-configurations (e.g. backrefs) will also have been made
- available for any mappers that were pending.
- Also constrast to :meth:`.MapperEvents.before_configured`,
- which is invoked before the series of mappers has been configured.
- This event can **only** be applied to the :class:`.Mapper` class
- or :func:`.mapper` function, and not to individual mappings or
- mapped classes. It is only invoked for all mappings as a whole::
- from sqlalchemy.orm import mapper
- @event.listens_for(mapper, "after_configured")
- def go():
- # ...
- Theoretically this event is called once per
- application, but is actually called any time new mappers
- have been affected by a :func:`.orm.configure_mappers`
- call. If new mappings are constructed after existing ones have
- already been used, this event will likely be called again. To ensure
- that a particular event is only called once and no further, the
- ``once=True`` argument (new in 0.9.4) can be applied::
- from sqlalchemy.orm import mapper
- @event.listens_for(mapper, "after_configured", once=True)
- def go():
- # ...
- .. seealso::
- :meth:`.MapperEvents.mapper_configured`
- :meth:`.MapperEvents.before_configured`
- """
- def before_insert(self, mapper, connection, target):
- """Receive an object instance before an INSERT statement
- is emitted corresponding to that instance.
- This event is used to modify local, non-object related
- attributes on the instance before an INSERT occurs, as well
- as to emit additional SQL statements on the given
- connection.
- The event is often called for a batch of objects of the
- same class before their INSERT statements are emitted at
- once in a later step. In the extremely rare case that
- this is not desirable, the :func:`.mapper` can be
- configured with ``batch=False``, which will cause
- batches of instances to be broken up into individual
- (and more poorly performing) event->persist->event
- steps.
- .. warning::
- Mapper-level flush events only allow **very limited operations**,
- on attributes local to the row being operated upon only,
- as well as allowing any SQL to be emitted on the given
- :class:`.Connection`. **Please read fully** the notes
- at :ref:`session_persistence_mapper` for guidelines on using
- these methods; generally, the :meth:`.SessionEvents.before_flush`
- method should be preferred for general on-flush changes.
- :param mapper: the :class:`.Mapper` which is the target
- of this event.
- :param connection: the :class:`.Connection` being used to
- emit INSERT statements for this instance. This
- provides a handle into the current transaction on the
- target database specific to this instance.
- :param target: the mapped instance being persisted. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :return: No return value is supported by this event.
- .. seealso::
- :ref:`session_persistence_events`
- """
- def after_insert(self, mapper, connection, target):
- """Receive an object instance after an INSERT statement
- is emitted corresponding to that instance.
- This event is used to modify in-Python-only
- state on the instance after an INSERT occurs, as well
- as to emit additional SQL statements on the given
- connection.
- The event is often called for a batch of objects of the
- same class after their INSERT statements have been
- emitted at once in a previous step. In the extremely
- rare case that this is not desirable, the
- :func:`.mapper` can be configured with ``batch=False``,
- which will cause batches of instances to be broken up
- into individual (and more poorly performing)
- event->persist->event steps.
- .. warning::
- Mapper-level flush events only allow **very limited operations**,
- on attributes local to the row being operated upon only,
- as well as allowing any SQL to be emitted on the given
- :class:`.Connection`. **Please read fully** the notes
- at :ref:`session_persistence_mapper` for guidelines on using
- these methods; generally, the :meth:`.SessionEvents.before_flush`
- method should be preferred for general on-flush changes.
- :param mapper: the :class:`.Mapper` which is the target
- of this event.
- :param connection: the :class:`.Connection` being used to
- emit INSERT statements for this instance. This
- provides a handle into the current transaction on the
- target database specific to this instance.
- :param target: the mapped instance being persisted. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :return: No return value is supported by this event.
- .. seealso::
- :ref:`session_persistence_events`
- """
- def before_update(self, mapper, connection, target):
- """Receive an object instance before an UPDATE statement
- is emitted corresponding to that instance.
- This event is used to modify local, non-object related
- attributes on the instance before an UPDATE occurs, as well
- as to emit additional SQL statements on the given
- connection.
- 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 :meth:`~.MapperEvents.before_update` is
- *not* a guarantee that an UPDATE statement will be
- issued, although you can affect the outcome here by
- modifying attributes so that a net change in value does
- exist.
- To detect if the column-based attributes on the object have net
- changes, and will therefore generate an UPDATE statement, use
- ``object_session(instance).is_modified(instance,
- include_collections=False)``.
- The event is often called for a batch of objects of the
- same class before their UPDATE statements are emitted at
- once in a later step. In the extremely rare case that
- this is not desirable, the :func:`.mapper` can be
- configured with ``batch=False``, which will cause
- batches of instances to be broken up into individual
- (and more poorly performing) event->persist->event
- steps.
- .. warning::
- Mapper-level flush events only allow **very limited operations**,
- on attributes local to the row being operated upon only,
- as well as allowing any SQL to be emitted on the given
- :class:`.Connection`. **Please read fully** the notes
- at :ref:`session_persistence_mapper` for guidelines on using
- these methods; generally, the :meth:`.SessionEvents.before_flush`
- method should be preferred for general on-flush changes.
- :param mapper: the :class:`.Mapper` which is the target
- of this event.
- :param connection: the :class:`.Connection` being used to
- emit UPDATE statements for this instance. This
- provides a handle into the current transaction on the
- target database specific to this instance.
- :param target: the mapped instance being persisted. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :return: No return value is supported by this event.
- .. seealso::
- :ref:`session_persistence_events`
- """
- def after_update(self, mapper, connection, target):
- """Receive an object instance after an UPDATE statement
- is emitted corresponding to that instance.
- This event is used to modify in-Python-only
- state on the instance after an UPDATE occurs, as well
- as to emit additional SQL statements on the given
- connection.
- This method is called for all instances that are
- marked as "dirty", *even those which have no net changes
- to their column-based attributes*, and for which
- no UPDATE statement has proceeded. 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 :meth:`~.MapperEvents.after_update` is
- *not* a guarantee that an UPDATE statement has been
- issued.
- To detect if the column-based attributes on the object have net
- changes, and therefore resulted in an UPDATE statement, use
- ``object_session(instance).is_modified(instance,
- include_collections=False)``.
- The event is often called for a batch of objects of the
- same class after their UPDATE statements have been emitted at
- once in a previous step. In the extremely rare case that
- this is not desirable, the :func:`.mapper` can be
- configured with ``batch=False``, which will cause
- batches of instances to be broken up into individual
- (and more poorly performing) event->persist->event
- steps.
- .. warning::
- Mapper-level flush events only allow **very limited operations**,
- on attributes local to the row being operated upon only,
- as well as allowing any SQL to be emitted on the given
- :class:`.Connection`. **Please read fully** the notes
- at :ref:`session_persistence_mapper` for guidelines on using
- these methods; generally, the :meth:`.SessionEvents.before_flush`
- method should be preferred for general on-flush changes.
- :param mapper: the :class:`.Mapper` which is the target
- of this event.
- :param connection: the :class:`.Connection` being used to
- emit UPDATE statements for this instance. This
- provides a handle into the current transaction on the
- target database specific to this instance.
- :param target: the mapped instance being persisted. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :return: No return value is supported by this event.
- .. seealso::
- :ref:`session_persistence_events`
- """
- def before_delete(self, mapper, connection, target):
- """Receive an object instance before a DELETE statement
- is emitted corresponding to that instance.
- This event is used to emit additional SQL statements on
- the given connection as well as to perform application
- specific bookkeeping related to a deletion event.
- The event is often called for a batch of objects of the
- same class before their DELETE statements are emitted at
- once in a later step.
- .. warning::
- Mapper-level flush events only allow **very limited operations**,
- on attributes local to the row being operated upon only,
- as well as allowing any SQL to be emitted on the given
- :class:`.Connection`. **Please read fully** the notes
- at :ref:`session_persistence_mapper` for guidelines on using
- these methods; generally, the :meth:`.SessionEvents.before_flush`
- method should be preferred for general on-flush changes.
- :param mapper: the :class:`.Mapper` which is the target
- of this event.
- :param connection: the :class:`.Connection` being used to
- emit DELETE statements for this instance. This
- provides a handle into the current transaction on the
- target database specific to this instance.
- :param target: the mapped instance being deleted. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :return: No return value is supported by this event.
- .. seealso::
- :ref:`session_persistence_events`
- """
- def after_delete(self, mapper, connection, target):
- """Receive an object instance after a DELETE statement
- has been emitted corresponding to that instance.
- This event is used to emit additional SQL statements on
- the given connection as well as to perform application
- specific bookkeeping related to a deletion event.
- The event is often called for a batch of objects of the
- same class after their DELETE statements have been emitted at
- once in a previous step.
- .. warning::
- Mapper-level flush events only allow **very limited operations**,
- on attributes local to the row being operated upon only,
- as well as allowing any SQL to be emitted on the given
- :class:`.Connection`. **Please read fully** the notes
- at :ref:`session_persistence_mapper` for guidelines on using
- these methods; generally, the :meth:`.SessionEvents.before_flush`
- method should be preferred for general on-flush changes.
- :param mapper: the :class:`.Mapper` which is the target
- of this event.
- :param connection: the :class:`.Connection` being used to
- emit DELETE statements for this instance. This
- provides a handle into the current transaction on the
- target database specific to this instance.
- :param target: the mapped instance being deleted. If
- the event is configured with ``raw=True``, this will
- instead be the :class:`.InstanceState` state-management
- object associated with the instance.
- :return: No return value is supported by this event.
- .. seealso::
- :ref:`session_persistence_events`
- """
- class _MapperEventsHold(_EventsHold):
- all_holds = weakref.WeakKeyDictionary()
- def resolve(self, class_):
- return _mapper_or_none(class_)
- class HoldMapperEvents(_EventsHold.HoldEvents, MapperEvents):
- pass
- dispatch = event.dispatcher(HoldMapperEvents)
- class SessionEvents(event.Events):
- """Define events specific to :class:`.Session` lifecycle.
- e.g.::
- from sqlalchemy import event
- from sqlalchemy.orm import sessionmaker
- def my_before_commit(session):
- print "before commit!"
- Session = sessionmaker()
- event.listen(Session, "before_commit", my_before_commit)
- The :func:`~.event.listen` function will accept
- :class:`.Session` objects as well as the return result
- of :class:`~.sessionmaker()` and :class:`~.scoped_session()`.
- Additionally, it accepts the :class:`.Session` class which
- will apply listeners to all :class:`.Session` instances
- globally.
- """
- _target_class_doc = "SomeSessionOrFactory"
- _dispatch_target = Session
- @classmethod
- def _accept_with(cls, target):
- if isinstance(target, scoped_session):
- target = target.session_factory
- if not isinstance(target, sessionmaker) and \
- (
- not isinstance(target, type) or
- not issubclass(target, Session)
- ):
- raise exc.ArgumentError(
- "Session event listen on a scoped_session "
- "requires that its creation callable "
- "is associated with the Session class.")
- if isinstance(target, sessionmaker):
- return target.class_
- elif isinstance(target, type):
- if issubclass(target, scoped_session):
- return Session
- elif issubclass(target, Session):
- return target
- elif isinstance(target, Session):
- return target
- else:
- return None
- def after_transaction_create(self, session, transaction):
- """Execute when a new :class:`.SessionTransaction` is created.
- This event differs from :meth:`~.SessionEvents.after_begin`
- in that it occurs for each :class:`.SessionTransaction`
- overall, as opposed to when transactions are begun
- on individual database connections. It is also invoked
- for nested transactions and subtransactions, and is always
- matched by a corresponding
- :meth:`~.SessionEvents.after_transaction_end` event
- (assuming normal operation of the :class:`.Session`).
- :param session: the target :class:`.Session`.
- :param transaction: the target :class:`.SessionTransaction`.
- To detect if this is the outermost
- :class:`.SessionTransaction`, as opposed to a "subtransaction" or a
- SAVEPOINT, test that the :attr:`.SessionTransaction.parent` attribute
- is ``None``::
- @event.listens_for(session, "after_transaction_create")
- def after_transaction_create(session, transaction):
- if transaction.parent is None:
- # work with top-level transaction
- To detect if the :class:`.SessionTransaction` is a SAVEPOINT, use the
- :attr:`.SessionTransaction.nested` attribute::
- @event.listens_for(session, "after_transaction_create")
- def after_transaction_create(session, transaction):
- if transaction.nested:
- # work with SAVEPOINT transaction
- .. seealso::
- :class:`.SessionTransaction`
- :meth:`~.SessionEvents.after_transaction_end`
- """
- def after_transaction_end(self, session, transaction):
- """Execute when the span of a :class:`.SessionTransaction` ends.
- This event differs from :meth:`~.SessionEvents.after_commit`
- in that it corresponds to all :class:`.SessionTransaction`
- objects in use, including those for nested transactions
- and subtransactions, and is always matched by a corresponding
- :meth:`~.SessionEvents.after_transaction_create` event.
- :param session: the target :class:`.Session`.
- :param transaction: the target :class:`.SessionTransaction`.
- To detect if this is the outermost
- :class:`.SessionTransaction`, as opposed to a "subtransaction" or a
- SAVEPOINT, test that the :attr:`.SessionTransaction.parent` attribute
- is ``None``::
- @event.listens_for(session, "after_transaction_create")
- def after_transaction_end(session, transaction):
- if transaction.parent is None:
- # work with top-level transaction
- To detect if the :class:`.SessionTransaction` is a SAVEPOINT, use the
- :attr:`.SessionTransaction.nested` attribute::
- @event.listens_for(session, "after_transaction_create")
- def after_transaction_end(session, transaction):
- if transaction.nested:
- # work with SAVEPOINT transaction
- .. seealso::
- :class:`.SessionTransaction`
- :meth:`~.SessionEvents.after_transaction_create`
- """
- def before_commit(self, session):
- """Execute before commit is called.
- .. note::
- The :meth:`~.SessionEvents.before_commit` hook is *not* per-flush,
- that is, the :class:`.Session` can emit SQL to the database
- many times within the scope of a transaction.
- For interception of these events, use the
- :meth:`~.SessionEvents.before_flush`,
- :meth:`~.SessionEvents.after_flush`, or
- :meth:`~.SessionEvents.after_flush_postexec`
- events.
- :param session: The target :class:`.Session`.
- .. seealso::
- :meth:`~.SessionEvents.after_commit`
- :meth:`~.SessionEvents.after_begin`
- :meth:`~.SessionEvents.after_transaction_create`
- :meth:`~.SessionEvents.after_transaction_end`
- """
- def after_commit(self, session):
- """Execute after a commit has occurred.
- .. note::
- The :meth:`~.SessionEvents.after_commit` hook is *not* per-flush,
- that is, the :class:`.Session` can emit SQL to the database
- many times within the scope of a transaction.
- For interception of these events, use the
- :meth:`~.SessionEvents.before_flush`,
- :meth:`~.SessionEvents.after_flush`, or
- :meth:`~.SessionEvents.after_flush_postexec`
- events.
- .. note::
- The :class:`.Session` is not in an active transaction
- when the :meth:`~.SessionEvents.after_commit` event is invoked,
- and therefore can not emit SQL. To emit SQL corresponding to
- every transaction, use the :meth:`~.SessionEvents.before_commit`
- event.
- :param session: The target :class:`.Session`.
- .. seealso::
- :meth:`~.SessionEvents.before_commit`
- :meth:`~.SessionEvents.after_begin`
- :meth:`~.SessionEvents.after_transaction_create`
- :meth:`~.SessionEvents.after_transaction_end`
- """
- def after_rollback(self, session):
- """Execute after a real DBAPI rollback has occurred.
- Note that this event only fires when the *actual* rollback against
- the database occurs - it does *not* fire each time the
- :meth:`.Session.rollback` method is called, if the underlying
- DBAPI transaction has already been rolled back. In many
- cases, the :class:`.Session` will not be in
- an "active" state during this event, as the current
- transaction is not valid. To acquire a :class:`.Session`
- which is active after the outermost rollback has proceeded,
- use the :meth:`.SessionEvents.after_soft_rollback` event, checking the
- :attr:`.Session.is_active` flag.
- :param session: The target :class:`.Session`.
- """
- def after_soft_rollback(self, session, previous_transaction):
- """Execute after any rollback has occurred, including "soft"
- rollbacks that don't actually emit at the DBAPI level.
- This corresponds to both nested and outer rollbacks, i.e.
- the innermost rollback that calls the DBAPI's
- rollback() method, as well as the enclosing rollback
- calls that only pop themselves from the transaction stack.
- The given :class:`.Session` can be used to invoke SQL and
- :meth:`.Session.query` operations after an outermost rollback
- by first checking the :attr:`.Session.is_active` flag::
- @event.listens_for(Session, "after_soft_rollback")
- def do_something(session, previous_transaction):
- if session.is_active:
- session.execute("select * from some_table")
- :param session: The target :class:`.Session`.
- :param previous_transaction: The :class:`.SessionTransaction`
- transactional marker object which was just closed. The current
- :class:`.SessionTransaction` for the given :class:`.Session` is
- available via the :attr:`.Session.transaction` attribute.
- .. versionadded:: 0.7.3
- """
- def before_flush(self, session, flush_context, instances):
- """Execute before flush process has started.
- :param session: The target :class:`.Session`.
- :param flush_context: Internal :class:`.UOWTransaction` object
- which handles the details of the flush.
- :param instances: Usually ``None``, this is the collection of
- objects which can be passed to the :meth:`.Session.flush` method
- (note this usage is deprecated).
- .. seealso::
- :meth:`~.SessionEvents.after_flush`
- :meth:`~.SessionEvents.after_flush_postexec`
- :ref:`session_persistence_events`
- """
- 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.
- :param session: The target :class:`.Session`.
- :param flush_context: Internal :class:`.UOWTransaction` object
- which handles the details of the flush.
- .. seealso::
- :meth:`~.SessionEvents.before_flush`
- :meth:`~.SessionEvents.after_flush_postexec`
- :ref:`session_persistence_events`
- """
- def after_flush_postexec(self, session, flush_context):
- """Execute after flush has completed, and after the post-exec
- state occurs.
- This will be when the 'new', 'dirty', and 'deleted' lists are in
- their final state. An actual commit() may or may not have
- occurred, depending on whether or not the flush started its own
- transaction or participated in a larger transaction.
- :param session: The target :class:`.Session`.
- :param flush_context: Internal :class:`.UOWTransaction` object
- which handles the details of the flush.
- .. seealso::
- :meth:`~.SessionEvents.before_flush`
- :meth:`~.SessionEvents.after_flush`
- :ref:`session_persistence_events`
- """
- def after_begin(self, session, transaction, connection):
- """Execute after a transaction is begun on a connection
- :param session: The target :class:`.Session`.
- :param transaction: The :class:`.SessionTransaction`.
- :param connection: The :class:`~.engine.Connection` object
- which will be used for SQL statements.
- .. seealso::
- :meth:`~.SessionEvents.before_commit`
- :meth:`~.SessionEvents.after_commit`
- :meth:`~.SessionEvents.after_transaction_create`
- :meth:`~.SessionEvents.after_transaction_end`
- """
- def before_attach(self, session, instance):
- """Execute before an instance is attached to a session.
- This is called before an add, delete or merge causes
- the object to be part of the session.
- .. versionadded:: 0.8. Note that :meth:`~.SessionEvents.after_attach`
- now fires off after the item is part of the session.
- :meth:`.before_attach` is provided for those cases where
- the item should not yet be part of the session state.
- .. seealso::
- :meth:`~.SessionEvents.after_attach`
- :ref:`session_lifecycle_events`
- """
- def after_attach(self, session, instance):
- """Execute after an instance is attached to a session.
- This is called after an add, delete or merge.
- .. note::
- As of 0.8, this event fires off *after* the item
- has been fully associated with the session, which is
- different than previous releases. For event
- handlers that require the object not yet
- be part of session state (such as handlers which
- may autoflush while the target object is not
- yet complete) consider the
- new :meth:`.before_attach` event.
- .. seealso::
- :meth:`~.SessionEvents.before_attach`
- :ref:`session_lifecycle_events`
- """
- @event._legacy_signature("0.9",
- ["session", "query", "query_context", "result"],
- lambda update_context: (
- update_context.session,
- update_context.query,
- update_context.context,
- update_context.result))
- def after_bulk_update(self, update_context):
- """Execute after a bulk update operation to the session.
- This is called as a result of the :meth:`.Query.update` method.
- :param update_context: an "update context" object which contains
- details about the update, including these attributes:
- * ``session`` - the :class:`.Session` involved
- * ``query`` -the :class:`.Query` object that this update operation
- was called upon.
- * ``context`` The :class:`.QueryContext` object, corresponding
- to the invocation of an ORM query.
- * ``result`` the :class:`.ResultProxy` returned as a result of the
- bulk UPDATE operation.
- """
- @event._legacy_signature("0.9",
- ["session", "query", "query_context", "result"],
- lambda delete_context: (
- delete_context.session,
- delete_context.query,
- delete_context.context,
- delete_context.result))
- def after_bulk_delete(self, delete_context):
- """Execute after a bulk delete operation to the session.
- This is called as a result of the :meth:`.Query.delete` method.
- :param delete_context: a "delete context" object which contains
- details about the update, including these attributes:
- * ``session`` - the :class:`.Session` involved
- * ``query`` -the :class:`.Query` object that this update operation
- was called upon.
- * ``context`` The :class:`.QueryContext` object, corresponding
- to the invocation of an ORM query.
- * ``result`` the :class:`.ResultProxy` returned as a result of the
- bulk DELETE operation.
- """
- def transient_to_pending(self, session, instance):
- """Intercept the "transient to pending" transition for a specific object.
- This event is a specialization of the
- :meth:`.SessionEvents.after_attach` event which is only invoked
- for this specific transition. It is invoked typically during the
- :meth:`.Session.add` call.
- :param session: target :class:`.Session`
- :param instance: the ORM-mapped instance being operated upon.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def pending_to_transient(self, session, instance):
- """Intercept the "pending to transient" transition for a specific object.
- This less common transition occurs when an pending object that has
- not been flushed is evicted from the session; this can occur
- when the :meth:`.Session.rollback` method rolls back the transaction,
- or when the :meth:`.Session.expunge` method is used.
- :param session: target :class:`.Session`
- :param instance: the ORM-mapped instance being operated upon.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def persistent_to_transient(self, session, instance):
- """Intercept the "persistent to transient" transition for a specific object.
- This less common transition occurs when an pending object that has
- has been flushed is evicted from the session; this can occur
- when the :meth:`.Session.rollback` method rolls back the transaction.
- :param session: target :class:`.Session`
- :param instance: the ORM-mapped instance being operated upon.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def pending_to_persistent(self, session, instance):
- """Intercept the "pending to persistent"" transition for a specific object.
- This event is invoked within the flush process, and is
- similar to scanning the :attr:`.Session.new` collection within
- the :meth:`.SessionEvents.after_flush` event. However, in this
- case the object has already been moved to the persistent state
- when the event is called.
- :param session: target :class:`.Session`
- :param instance: the ORM-mapped instance being operated upon.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def detached_to_persistent(self, session, instance):
- """Intercept the "detached to persistent" transition for a specific object.
- This event is a specialization of the
- :meth:`.SessionEvents.after_attach` event which is only invoked
- for this specific transition. It is invoked typically during the
- :meth:`.Session.add` call, as well as during the
- :meth:`.Session.delete` call if the object was not previously
- associated with the
- :class:`.Session` (note that an object marked as "deleted" remains
- in the "persistent" state until the flush proceeds).
- .. note::
- If the object becomes persistent as part of a call to
- :meth:`.Session.delete`, the object is **not** yet marked as
- deleted when this event is called. To detect deleted objects,
- check the ``deleted`` flag sent to the
- :meth:`.SessionEvents.persistent_to_detached` to event after the
- flush proceeds, or check the :attr:`.Session.deleted` collection
- within the :meth:`.SessionEvents.before_flush` event if deleted
- objects need to be intercepted before the flush.
- :param session: target :class:`.Session`
- :param instance: the ORM-mapped instance being operated upon.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def loaded_as_persistent(self, session, instance):
- """Intercept the "loaded as persistent" transition for a specific object.
- This event is invoked within the ORM loading process, and is invoked
- very similarly to the :meth:`.InstanceEvents.load` event. However,
- the event here is linkable to a :class:`.Session` class or instance,
- rather than to a mapper or class hierarchy, and integrates
- with the other session lifecycle events smoothly. The object
- is guaranteed to be present in the session's identity map when
- this event is called.
- :param session: target :class:`.Session`
- :param instance: the ORM-mapped instance being operated upon.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def persistent_to_deleted(self, session, instance):
- """Intercept the "persistent to deleted" transition for a specific object.
- This event is invoked when a persistent object's identity
- is deleted from the database within a flush, however the object
- still remains associated with the :class:`.Session` until the
- transaction completes.
- If the transaction is rolled back, the object moves again
- to the persistent state, and the
- :meth:`.SessionEvents.deleted_to_persistent` event is called.
- If the transaction is committed, the object becomes detached,
- which will emit the :meth:`.SessionEvents.deleted_to_detached`
- event.
- Note that while the :meth:`.Session.delete` method is the primary
- public interface to mark an object as deleted, many objects
- get deleted due to cascade rules, which are not always determined
- until flush time. Therefore, there's no way to catch
- every object that will be deleted until the flush has proceeded.
- the :meth:`.SessionEvents.persistent_to_deleted` event is therefore
- invoked at the end of a flush.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def deleted_to_persistent(self, session, instance):
- """Intercept the "deleted to persistent" transition for a specific object.
- This transition occurs only when an object that's been deleted
- successfully in a flush is restored due to a call to
- :meth:`.Session.rollback`. The event is not called under
- any other circumstances.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def deleted_to_detached(self, session, instance):
- """Intercept the "deleted to detached" transition for a specific object.
- This event is invoked when a deleted object is evicted
- from the session. The typical case when this occurs is when
- the transaction for a :class:`.Session` in which the object
- was deleted is committed; the object moves from the deleted
- state to the detached state.
- It is also invoked for objects that were deleted in a flush
- when the :meth:`.Session.expunge_all` or :meth:`.Session.close`
- events are called, as well as if the object is individually
- expunged from its deleted state via :meth:`.Session.expunge`.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- def persistent_to_detached(self, session, instance):
- """Intercept the "persistent to detached" transition for a specific object.
- This event is invoked when a persistent object is evicted
- from the session. There are many conditions that cause this
- to happen, including:
- * using a method such as :meth:`.Session.expunge`
- or :meth:`.Session.close`
- * Calling the :meth:`.Session.rollback` method, when the object
- was part of an INSERT statement for that session's transaction
- :param session: target :class:`.Session`
- :param instance: the ORM-mapped instance being operated upon.
- :param deleted: boolean. If True, indicates this object moved
- to the detached state because it was marked as deleted and flushed.
- .. versionadded:: 1.1
- .. seealso::
- :ref:`session_lifecycle_events`
- """
- class AttributeEvents(event.Events):
- """Define events for object attributes.
- These are typically defined on the class-bound descriptor for the
- target class.
- e.g.::
- from sqlalchemy import event
- def my_append_listener(target, value, initiator):
- print "received append event for target: %s" % target
- event.listen(MyClass.collection, 'append', my_append_listener)
- Listeners have the option to return a possibly modified version
- of the value, when the ``retval=True`` flag is passed
- to :func:`~.event.listen`::
- def validate_phone(target, value, oldvalue, initiator):
- "Strip non-numeric characters from a phone number"
- return re.sub(r'\D', '', value)
- # setup listener on UserContact.phone attribute, instructing
- # it to use the return value
- listen(UserContact.phone, 'set', validate_phone, retval=True)
- A validation function like the above can also raise an exception
- such as :exc:`ValueError` to halt the operation.
- Several modifiers are available to the :func:`~.event.listen` function.
- :param active_history=False: When True, indicates that the
- "set" event would like to receive the "old" value being
- replaced unconditionally, even if this requires firing off
- database loads. Note that ``active_history`` can also be
- set directly via :func:`.column_property` and
- :func:`.relationship`.
- :param propagate=False: When True, the listener function will
- be established not just for the class attribute given, but
- for attributes of the same name on all current subclasses
- of that class, as well as all future subclasses of that
- class, using an additional listener that listens for
- instrumentation events.
- :param raw=False: When True, the "target" argument to the
- event will be the :class:`.InstanceState` management
- object, rather than the mapped instance itself.
- :param retval=False: when True, the user-defined event
- listening must return the "value" argument from the
- function. This gives the listening function the opportunity
- to change the value that is ultimately used for a "set"
- or "append" event.
- """
- _target_class_doc = "SomeClass.some_attribute"
- _dispatch_target = QueryableAttribute
- @staticmethod
- def _set_dispatch(cls, dispatch_cls):
- dispatch = event.Events._set_dispatch(cls, dispatch_cls)
- dispatch_cls._active_history = False
- return dispatch
- @classmethod
- def _accept_with(cls, target):
- # TODO: coverage
- if isinstance(target, interfaces.MapperProperty):
- return getattr(target.parent.class_, target.key)
- else:
- return target
- @classmethod
- def _listen(cls, event_key, active_history=False,
- raw=False, retval=False,
- propagate=False):
- target, identifier, fn = \
- event_key.dispatch_target, event_key.identifier, \
- event_key._listen_fn
- if active_history:
- target.dispatch._active_history = True
- if not raw or not retval:
- def wrap(target, value, *arg):
- if not raw:
- target = target.obj()
- if not retval:
- fn(target, value, *arg)
- return value
- else:
- return fn(target, value, *arg)
- event_key = event_key.with_wrapper(wrap)
- event_key.base_listen(propagate=propagate)
- if propagate:
- manager = instrumentation.manager_of_class(target.class_)
- for mgr in manager.subclass_managers(True):
- event_key.with_dispatch_target(
- mgr[target.key]).base_listen(propagate=True)
- def append(self, target, value, initiator):
- """Receive a collection append event.
- :param target: the object instance receiving the event.
- If the listener is registered with ``raw=True``, this will
- be the :class:`.InstanceState` object.
- :param value: the value being appended. If this listener
- is registered with ``retval=True``, the listener
- function must return this value, or a new value which
- replaces it.
- :param initiator: An instance of :class:`.attributes.Event`
- representing the initiation of the event. May be modified
- from its original value by backref handlers in order to control
- chained event propagation.
- .. versionchanged:: 0.9.0 the ``initiator`` argument is now
- passed as a :class:`.attributes.Event` object, and may be
- modified by backref handlers within a chain of backref-linked
- events.
- :return: if the event was registered with ``retval=True``,
- the given value, or a new effective value, should be returned.
- """
- def remove(self, target, value, initiator):
- """Receive a collection remove event.
- :param target: the object instance receiving the event.
- If the listener is registered with ``raw=True``, this will
- be the :class:`.InstanceState` object.
- :param value: the value being removed.
- :param initiator: An instance of :class:`.attributes.Event`
- representing the initiation of the event. May be modified
- from its original value by backref handlers in order to control
- chained event propagation.
- .. versionchanged:: 0.9.0 the ``initiator`` argument is now
- passed as a :class:`.attributes.Event` object, and may be
- modified by backref handlers within a chain of backref-linked
- events.
- :return: No return value is defined for this event.
- """
- def set(self, target, value, oldvalue, initiator):
- """Receive a scalar set event.
- :param target: the object instance receiving the event.
- If the listener is registered with ``raw=True``, this will
- be the :class:`.InstanceState` object.
- :param value: the value being set. If this listener
- is registered with ``retval=True``, the listener
- function must return this value, or a new value which
- replaces it.
- :param oldvalue: the previous value being replaced. This
- may also be the symbol ``NEVER_SET`` or ``NO_VALUE``.
- If the listener is registered with ``active_history=True``,
- the previous value of the attribute will be loaded from
- the database if the existing value is currently unloaded
- or expired.
- :param initiator: An instance of :class:`.attributes.Event`
- representing the initiation of the event. May be modified
- from its original value by backref handlers in order to control
- chained event propagation.
- .. versionchanged:: 0.9.0 the ``initiator`` argument is now
- passed as a :class:`.attributes.Event` object, and may be
- modified by backref handlers within a chain of backref-linked
- events.
- :return: if the event was registered with ``retval=True``,
- the given value, or a new effective value, should be returned.
- """
- def init_scalar(self, target, value, dict_):
- """Receive a scalar "init" event.
- This event is invoked when an uninitialized, unpersisted scalar
- attribute is accessed. A value of ``None`` is typically returned
- in this case; no changes are made to the object's state.
- The event handler can alter this behavior in two ways.
- One is that a value other than ``None`` may be returned. The other
- is that the value may be established as part of the object's state,
- which will also have the effect that it is persisted.
- Typical use is to establish a specific default value of an attribute
- upon access::
- SOME_CONSTANT = 3.1415926
- @event.listens_for(
- MyClass.some_attribute, "init_scalar",
- retval=True, propagate=True)
- def _init_some_attribute(target, dict_, value):
- dict_['some_attribute'] = SOME_CONSTANT
- return SOME_CONSTANT
- Above, we initialize the attribute ``MyClass.some_attribute`` to the
- value of ``SOME_CONSTANT``. The above code includes the following
- features:
- * By setting the value ``SOME_CONSTANT`` in the given ``dict_``,
- we indicate that the value is to be persisted to the database.
- **The given value is only persisted to the database if we
- explicitly associate it with the object**. The ``dict_`` given
- is the ``__dict__`` element of the mapped object, assuming the
- default attribute instrumentation system is in place.
- * By establishing the ``retval=True`` flag, the value we return
- from the function will be returned by the attribute getter.
- Without this flag, the event is assumed to be a passive observer
- and the return value of our function is ignored.
- * The ``propagate=True`` flag is significant if the mapped class
- includes inheriting subclasses, which would also make use of this
- event listener. Without this flag, an inheriting subclass will
- not use our event handler.
- When we establish the value in the given dictionary, the value will
- be used in the INSERT statement established by the unit of work.
- Normally, the default returned value of ``None`` is not established as
- part of the object, to avoid the issue of mutations occurring to the
- object in response to a normally passive "get" operation, and also
- sidesteps the issue of whether or not the :meth:`.AttributeEvents.set`
- event should be awkwardly fired off during an attribute access
- operation. This does not impact the INSERT operation since the
- ``None`` value matches the value of ``NULL`` that goes into the
- database in any case; note that ``None`` is skipped during the INSERT
- to ensure that column and SQL-level default functions can fire off.
- The attribute set event :meth:`.AttributeEvents.set` as well as the
- related validation feature provided by :obj:`.orm.validates` is
- **not** invoked when we apply our value to the given ``dict_``. To
- have these events to invoke in response to our newly generated
- value, apply the value to the given object as a normal attribute
- set operation::
- SOME_CONSTANT = 3.1415926
- @event.listens_for(
- MyClass.some_attribute, "init_scalar",
- retval=True, propagate=True)
- def _init_some_attribute(target, dict_, value):
- # will also fire off attribute set events
- target.some_attribute = SOME_CONSTANT
- return SOME_CONSTANT
- When multiple listeners are set up, the generation of the value
- is "chained" from one listener to the next by passing the value
- returned by the previous listener that specifies ``retval=True``
- as the ``value`` argument of the next listener.
- The :meth:`.AttributeEvents.init_scalar` event may be used to
- extract values from the default values and/or callables established on
- mapped :class:`.Column` objects. See the "active column defaults"
- example in :ref:`examples_instrumentation` for an example of this.
- .. versionadded:: 1.1
- :param target: the object instance receiving the event.
- If the listener is registered with ``raw=True``, this will
- be the :class:`.InstanceState` object.
- :param value: the value that is to be returned before this event
- listener were invoked. This value begins as the value ``None``,
- however will be the return value of the previous event handler
- function if multiple listeners are present.
- :param dict_: the attribute dictionary of this mapped object.
- This is normally the ``__dict__`` of the object, but in all cases
- represents the destination that the attribute system uses to get
- at the actual value of this attribute. Placing the value in this
- dictionary has the effect that the value will be used in the
- INSERT statement generated by the unit of work.
- .. seealso::
- :ref:`examples_instrumentation` - see the
- ``active_column_defaults.py`` example.
- """
- def init_collection(self, target, collection, collection_adapter):
- """Receive a 'collection init' event.
- This event is triggered for a collection-based attribute, when
- the initial "empty collection" is first generated for a blank
- attribute, as well as for when the collection is replaced with
- a new one, such as via a set event.
- E.g., given that ``User.addresses`` is a relationship-based
- collection, the event is triggered here::
- u1 = User()
- u1.addresses.append(a1) # <- new collection
- and also during replace operations::
- u1.addresses = [a2, a3] # <- new collection
- :param target: the object instance receiving the event.
- If the listener is registered with ``raw=True``, this will
- be the :class:`.InstanceState` object.
- :param collection: the new collection. This will always be generated
- from what was specified as
- :paramref:`.RelationshipProperty.collection_class`, and will always
- be empty.
- :param collection_adpater: the :class:`.CollectionAdapter` that will
- mediate internal access to the collection.
- .. versionadded:: 1.0.0 the :meth:`.AttributeEvents.init_collection`
- and :meth:`.AttributeEvents.dispose_collection` events supersede
- the :class:`.collection.linker` hook.
- """
- def dispose_collection(self, target, collection, collection_adpater):
- """Receive a 'collection dispose' event.
- This event is triggered for a collection-based attribute when
- a collection is replaced, that is::
- u1.addresses.append(a1)
- u1.addresses = [a2, a3] # <- old collection is disposed
- The mechanics of the event will typically include that the given
- collection is empty, even if it stored objects while being replaced.
- .. versionadded:: 1.0.0 the :meth:`.AttributeEvents.init_collection`
- and :meth:`.AttributeEvents.dispose_collection` events supersede
- the :class:`.collection.linker` hook.
- """
- class QueryEvents(event.Events):
- """Represent events within the construction of a :class:`.Query` object.
- The events here are intended to be used with an as-yet-unreleased
- inspection system for :class:`.Query`. Some very basic operations
- are possible now, however the inspection system is intended to allow
- complex query manipulations to be automated.
- .. versionadded:: 1.0.0
- """
- _target_class_doc = "SomeQuery"
- _dispatch_target = Query
- def before_compile(self, query):
- """Receive the :class:`.Query` object before it is composed into a
- core :class:`.Select` object.
- This event is intended to allow changes to the query given::
- @event.listens_for(Query, "before_compile", retval=True)
- def no_deleted(query):
- for desc in query.column_descriptions:
- if desc['type'] is User:
- entity = desc['entity']
- query = query.filter(entity.deleted == False)
- return query
- The event should normally be listened with the ``retval=True``
- parameter set, so that the modified query may be returned.
- """
- @classmethod
- def _listen(
- cls, event_key, retval=False, **kw):
- fn = event_key._listen_fn
- if not retval:
- def wrap(*arg, **kw):
- if not retval:
- query = arg[0]
- fn(*arg, **kw)
- return query
- else:
- return fn(*arg, **kw)
- event_key = event_key.with_wrapper(wrap)
- event_key.base_listen(**kw)
|