diff options
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/engine/interfaces.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/result.py | 25 | ||||
| -rw-r--r-- | lib/sqlalchemy/event/api.py | 10 | ||||
| -rw-r--r-- | lib/sqlalchemy/event/legacy.py | 14 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/__init__.py | 8 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/events.py | 49 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/loading.py | 20 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 61 |
8 files changed, 141 insertions, 48 deletions
diff --git a/lib/sqlalchemy/engine/interfaces.py b/lib/sqlalchemy/engine/interfaces.py index f925df6c5..1baef29af 100644 --- a/lib/sqlalchemy/engine/interfaces.py +++ b/lib/sqlalchemy/engine/interfaces.py @@ -953,7 +953,7 @@ class CreateEnginePlugin(object): engine = create_engine( "mysql+pymysql://scott:tiger@localhost/test?plugin=myplugin") - Alternatively, the :paramref:`.create_engine.plugins" argument may be + Alternatively, the :paramref:`_sa.create_engine.plugins" argument may be passed as a list to :func:`_sa.create_engine`:: engine = create_engine( diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py index db546380e..621cba674 100644 --- a/lib/sqlalchemy/engine/result.py +++ b/lib/sqlalchemy/engine/result.py @@ -649,6 +649,11 @@ class Result(InPlaceGenerative): it will produce a new :class:`_engine.Result` object each time against its stored set of rows. + .. seealso:: + + :ref:`do_orm_execute_re_executing` - example usage within the + ORM to implement a result-set cache. + """ return FrozenResult(self) @@ -1173,12 +1178,28 @@ class FrozenResult(object): frozen = result.freeze() - r1 = frozen() - r2 = frozen() + unfrozen_result_one = frozen() + + for row in unfrozen_result_one: + print(row) + + unfrozen_result_two = frozen() + rows = unfrozen_result_two.all() + # ... etc .. versionadded:: 1.4 + .. seealso:: + + .. seealso:: + + :ref:`do_orm_execute_re_executing` - example usage within the + ORM to implement a result-set cache. + + :func:`_orm.loading.merge_frozen_result` - ORM function to merge + a frozen result back into a :class:`_orm.Session`. + """ def __init__(self, result): diff --git a/lib/sqlalchemy/event/api.py b/lib/sqlalchemy/event/api.py index 6bd63ceca..b36c448ce 100644 --- a/lib/sqlalchemy/event/api.py +++ b/lib/sqlalchemy/event/api.py @@ -61,9 +61,6 @@ def listen(target, identifier, fn, *args, **kw): event.listen(Mapper, "before_configure", on_config, once=True) - .. versionadded:: 0.9.4 Added ``once=True`` to :func:`.event.listen` - and :func:`.event.listens_for`. - .. warning:: The ``once`` argument does not imply automatic de-registration of the listener function after it has been invoked a first time; a listener entry will remain associated with the target object. @@ -128,9 +125,6 @@ def listens_for(target, identifier, *args, **kw): do_config() - .. versionadded:: 0.9.4 Added ``once=True`` to :func:`.event.listen` - and :func:`.event.listens_for`. - .. warning:: The ``once`` argument does not imply automatic de-registration of the listener function after it has been invoked a first time; a listener entry will remain associated with the target object. @@ -173,8 +167,6 @@ def remove(target, identifier, fn): propagated to subclasses of ``SomeMappedClass``; the :func:`.remove` function will revert all of these operations. - .. versionadded:: 0.9.0 - .. note:: The :func:`.remove` function cannot be called at the same time @@ -206,8 +198,6 @@ def remove(target, identifier, fn): def contains(target, identifier, fn): """Return True if the given target/ident/fn is set up to listen. - .. versionadded:: 0.9.0 - """ return _event_key(target, identifier, fn).contains() diff --git a/lib/sqlalchemy/event/legacy.py b/lib/sqlalchemy/event/legacy.py index f63c7d101..14115d377 100644 --- a/lib/sqlalchemy/event/legacy.py +++ b/lib/sqlalchemy/event/legacy.py @@ -96,8 +96,7 @@ def _standard_listen_example(dispatch_collection, sample_target, fn): else: current_since = None text = ( - "from sqlalchemy import event\n\n" - "# standard decorator style%(current_since)s\n" + "from sqlalchemy import event\n\n\n" "@event.listens_for(%(sample_target)s, '%(event_name)s')\n" "def receive_%(event_name)s(" "%(named_event_arguments)s%(has_kw_arguments)s):\n" @@ -105,17 +104,6 @@ def _standard_listen_example(dispatch_collection, sample_target, fn): "\n # ... (event handling logic) ...\n" ) - if len(dispatch_collection.arg_names) > 3: - text += ( - "\n# named argument style (new in 0.9)\n" - "@event.listens_for(" - "%(sample_target)s, '%(event_name)s', named=True)\n" - "def receive_%(event_name)s(**kw):\n" - " \"listen for the '%(event_name)s' event\"\n" - "%(example_kw_arg)s\n" - "\n # ... (event handling logic) ...\n" - ) - text %= { "current_since": " (arguments as of %s)" % current_since if current_since diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index 458103838..199ae11e5 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -272,6 +272,14 @@ def eagerload(*args, **kwargs): contains_alias = public_factory(AliasOption, ".orm.contains_alias") +if True: + from .events import AttributeEvents # noqa + from .events import MapperEvents # noqa + from .events import InstanceEvents # noqa + from .events import InstrumentationEvents # noqa + from .events import QueryEvents # noqa + from .events import SessionEvents # noqa + def __go(lcls): global __all__ diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py index 217aa76c7..ec907c63e 100644 --- a/lib/sqlalchemy/orm/events.py +++ b/lib/sqlalchemy/orm/events.py @@ -1427,7 +1427,27 @@ class SessionEvents(event.Events): .. seealso:: - :class:`.ORMExecuteState` + :ref:`session_execute_events` - top level documentation on how + to use :meth:`_orm.SessionEvents.do_orm_execute` + + :class:`.ORMExecuteState` - the object passed to the + :meth:`_orm.SessionEvents.do_orm_execute` event which contains + all information about the statement to be invoked. It also + provides an interface to extend the current statement, options, + and parameters as well as an option that allows programmatic + invocation of the statement at any point. + + :ref:`examples_session_orm_events` - includes examples of using + :meth:`_orm.SessionEvents.do_orm_execute` + + :ref:`examples_caching` - an example of how to integrate + Dogpile caching with the ORM :class:`_orm.Session` making use + of the :meth:`_orm.SessionEvents.do_orm_execute` event hook. + + :ref:`examples_sharding` - the Horizontal Sharding example / + extension relies upon the + :meth:`_orm.SessionEvents.do_orm_execute` event hook to invoke a + SQL statement on multiple backends and return a merged result. .. versionadded:: 1.4 @@ -2585,12 +2605,8 @@ class QueryEvents(event.Events): """Represent events within the construction of a :class:`_query.Query` object. - The events here are intended to be used with an as-yet-unreleased - inspection system for :class:`_query.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 + The :class:`_orm.QueryEvents` hooks are now superseded by the + :meth:`_orm.SessionEvents.do_orm_execute` event hook. """ @@ -2602,6 +2618,17 @@ class QueryEvents(event.Events): object before it is composed into a core :class:`_expression.Select` object. + .. deprecated:: 1.4 The :meth:`_orm.QueryEvents.before_compile` event + is superseded by the much more capable + :meth:`_orm.SessionEvents.do_orm_execute` hook. In version 1.4, + the :meth:`_orm.QueryEvents.before_compile` event is **no longer + used** for ORM-level attribute loads, such as loads of deferred + or expired attributes as well as relationship loaders. See the + new examples in :ref:`examples_session_orm_events` which + illustrate new ways of intercepting and modifying ORM queries + for the most common purpose of adding arbitrary filter criteria. + + This event is intended to allow changes to the query given:: @event.listens_for(Query, "before_compile", retval=True) @@ -2656,6 +2683,10 @@ class QueryEvents(event.Events): """Allow modifications to the :class:`_query.Query` object within :meth:`_query.Query.update`. + .. deprecated:: 1.4 The :meth:`_orm.QueryEvents.before_compile_update` + event is superseded by the much more capable + :meth:`_orm.SessionEvents.do_orm_execute` hook. + Like the :meth:`.QueryEvents.before_compile` event, if the event is to be used to alter the :class:`_query.Query` object, it should be configured with ``retval=True``, and the modified @@ -2702,6 +2733,10 @@ class QueryEvents(event.Events): """Allow modifications to the :class:`_query.Query` object within :meth:`_query.Query.delete`. + .. deprecated:: 1.4 The :meth:`_orm.QueryEvents.before_compile_delete` + event is superseded by the much more capable + :meth:`_orm.SessionEvents.do_orm_execute` hook. + Like the :meth:`.QueryEvents.before_compile` event, this event should be configured with ``retval=True``, and the modified :class:`_query.Query` object returned, as in :: diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index 601393156..2eb3e1368 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -141,6 +141,21 @@ def instances(cursor, context): @util.preload_module("sqlalchemy.orm.context") def merge_frozen_result(session, statement, frozen_result, load=True): + """Merge a :class:`_engine.FrozenResult` back into a :class:`_orm.Session`, + returning a new :class:`_engine.Result` object with :term:`persistent` + objects. + + See the section :ref:`do_orm_execute_re_executing` for an example. + + .. seealso:: + + :ref:`do_orm_execute_re_executing` + + :meth:`_engine.Result.freeze` + + :class:`_engine.FrozenResult` + + """ querycontext = util.preloaded.orm_context if load: @@ -184,6 +199,11 @@ def merge_frozen_result(session, statement, frozen_result, load=True): session.autoflush = autoflush +@util.deprecated( + "2.0", + "The :func:`_orm.merge_result` method is superseded by the " + ":func:`_orm.merge_frozen_result` function.", +) @util.preload_module("sqlalchemy.orm.context") def merge_result(query, iterator, load=True): """Merge a result into this :class:`.Query` object's Session.""" diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index e9d4ac2c6..ed1af0a80 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -107,6 +107,10 @@ class ORMExecuteState(util.MemoizedSlots): .. versionadded:: 1.4 + .. seealso:: + + :ref:`session_execute_events` - top level documentation on how + to use :meth:`_orm.SessionEvents.do_orm_execute` """ @@ -158,14 +162,24 @@ class ORMExecuteState(util.MemoizedSlots): bind_arguments=None, ): """Execute the statement represented by this - :class:`.ORMExecuteState`, without re-invoking events. - - This method essentially performs a re-entrant execution of the - current statement for which the :meth:`.SessionEvents.do_orm_execute` - event is being currently invoked. The use case for this is - for event handlers that want to override how the ultimate results - object is returned, such as for schemes that retrieve results from - an offline cache or which concatenate results from multiple executions. + :class:`.ORMExecuteState`, without re-invoking events that have + already proceeded. + + This method essentially performs a re-entrant execution of the current + statement for which the :meth:`.SessionEvents.do_orm_execute` event is + being currently invoked. The use case for this is for event handlers + that want to override how the ultimate + :class:`_engine.Result` object is returned, such as for schemes that + retrieve results from an offline cache or which concatenate results + from multiple executions. + + When the :class:`_engine.Result` object is returned by the actual + handler function within :meth:`_orm.SessionEvents.do_orm_execute` and + is propagated to the calling + :meth:`_orm.Session.execute` method, the remainder of the + :meth:`_orm.Session.execute` method is preempted and the + :class:`_engine.Result` object is returned to the caller of + :meth:`_orm.Session.execute` immediately. :param statement: optional statement to be invoked, in place of the statement currently represented by :attr:`.ORMExecuteState.statement`. @@ -970,13 +984,30 @@ class Session(_SessionClassMethods): transaction will load from the most recent database state. :param future: if True, use 2.0 style behavior for the - :meth:`_orm.Session.execute` method. This includes that the - :class:`_engine.Result` object returned will return new-style - tuple rows, as well as that Core constructs such as - :class:`_sql.Select`, - :class:`_sql.Update` and :class:`_sql.Delete` will be interpreted - in an ORM context if they are made against ORM entities rather than - plain :class:`.Table` metadata objects. + :meth:`_orm.Session.execute` method. Future mode includes the + following behaviors: + + * The :class:`_engine.Result` object returned by the + :meth:`_orm.Session.execute` method will return new-style tuple + :class:`_engine.Row` objects + + * The :meth:`_orm.Session.execute` method will invoke ORM style + queries given objects like :class:`_sql.Select`, + :class:`_sql.Update` and :class:`_sql.Delete` against ORM entities + + * The :class:`_orm.Session` will not use "bound" metadata in order + to locate an :class:`_engine.Engine`; the engine or engines in use + must be specified to the constructor of :class:`_orm.Session` or + otherwise be configured against the :class:`_orm.sessionmaker` + in use + + * The "subtransactions" feature of :meth:`_orm.Session.begin` is + removed in version 2.0 and is disabled when the future flag is + set. + + * The behavior of the :paramref:`_orm.relationship.cascade_backrefs` + flag on a :func:`_orm.relationship` will always assume + "False" behavior. The "future" flag is also available on a per-execution basis using the :paramref:`_orm.Session.execute.future` flag. |
