From d0295629cc671b52218a8b6da898bc1bea1a0a3c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 5 Aug 2017 21:46:24 -0400 Subject: [PATCH] - modernize orm.reconstructor documentation a bit Change-Id: Ied786e8f9ad78f524be03a382d002dada7dd218b (cherry picked from commit 4b4f8fbf25f1a5a76c1579c1a3fd6ffad07c8c66) --- doc/build/orm/constructors.rst | 30 +++++++++++++++++------------- lib/sqlalchemy/orm/events.py | 5 +++++ lib/sqlalchemy/orm/mapper.py | 6 ++++++ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/doc/build/orm/constructors.rst b/doc/build/orm/constructors.rst index 38cbb41820..02ce51ca65 100644 --- a/doc/build/orm/constructors.rst +++ b/doc/build/orm/constructors.rst @@ -18,10 +18,13 @@ then quietly restoring attributes directly on the instance rather than calling ``__init__``. If you need to do some setup on database-loaded instances before they're ready -to use, you can use the ``@reconstructor`` decorator to tag a method as the -ORM counterpart to ``__init__``. SQLAlchemy will call this method with no -arguments every time it loads or reconstructs one of your instances. This is -useful for recreating transient properties that are normally assigned in your +to use, there is an event hook known as :meth:`.InstanceEvents.load` which +can achieve this; it is also available via a class-specific decorator called +:func:`.orm.reconstructor`. When using :func:`.orm.reconstructor`, +the mapper will invoke the decorated method with no +arguments every time it loads or reconstructs an instance of the +class. This is +useful for recreating transient properties that are normally assigned in ``__init__``:: from sqlalchemy import orm @@ -36,21 +39,22 @@ useful for recreating transient properties that are normally assigned in your def init_on_load(self): self.stuff = [] -When ``obj = MyMappedClass()`` is executed, Python calls the ``__init__`` -method as normal and the ``data`` argument is required. When instances are +Above, when ``obj = MyMappedClass()`` is executed, the ``__init__`` constructor +is invoked normally and the ``data`` argument is required. When instances are loaded during a :class:`~sqlalchemy.orm.query.Query` operation as in ``query(MyMappedClass).one()``, ``init_on_load`` is called. -Any method may be tagged as the :func:`~sqlalchemy.orm.reconstructor`, even -the ``__init__`` method. SQLAlchemy will call the reconstructor method with no -arguments. Scalar (non-collection) database-mapped attributes of the instance -will be available for use within the function. Eagerly-loaded collections are -generally not yet available and will usually only contain the first element. +Any method may be tagged as the :func:`.orm.reconstructor`, even +the ``__init__`` method itself. It is invoked after all immediate +column-level attributes are loaded as well as after eagerly-loaded scalar +relationships. Eagerly loaded collections may be only partially populated +or not populated at all, depending on the kind of eager loading used. + ORM state changes made to objects at this stage will not be recorded for the -next flush() operation, so the activity within a reconstructor should be +next flush operation, so the activity within a reconstructor should be conservative. -:func:`~sqlalchemy.orm.reconstructor` is a shortcut into a larger system +:func:`.orm.reconstructor` is a shortcut into a larger system of "instance level" events, which can be subscribed to using the event API - see :class:`.InstanceEvents` for the full API description of these events. diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py index da801a09d9..f6eaccb02d 100644 --- a/lib/sqlalchemy/orm/events.py +++ b/lib/sqlalchemy/orm/events.py @@ -302,6 +302,9 @@ class InstanceEvents(event.Events): attributes and collections may or may not be loaded or even initialized, depending on what's present in the result rows. + The :meth:`.InstanceEvents.load` event is also available in a + class-method decorator format called :func:`.orm.reconstructor`. + :param target: the mapped instance. If the event is configured with ``raw=True``, this will instead be the :class:`.InstanceState` state-management @@ -317,6 +320,8 @@ class InstanceEvents(event.Events): :meth:`.InstanceEvents.refresh` + :ref:`mapping_constructors` + """ def refresh(self, target, context, attrs): diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 94c27aa0ef..5b2a5cbf6e 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2805,6 +2805,12 @@ def reconstructor(fn): this stage will not be recorded for the next flush() operation, so the activity within a reconstructor should be conservative. + .. seealso:: + + :ref:`mapping_constructors` + + :meth:`.InstanceEvents.load` + """ fn.__sa_reconstructor__ = True return fn -- 2.47.2