summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/orm/session.rst88
-rw-r--r--lib/sqlalchemy/orm/session.py48
2 files changed, 79 insertions, 57 deletions
diff --git a/doc/build/orm/session.rst b/doc/build/orm/session.rst
index 252a81742..47e17cbae 100644
--- a/doc/build/orm/session.rst
+++ b/doc/build/orm/session.rst
@@ -392,40 +392,61 @@ the ``save-update`` cascade. For more details see the section
Merging
-------
-:func:`~sqlalchemy.orm.session.Session.merge` reconciles the current state of
-an instance and its associated children with existing data in the database,
-and returns a copy of the instance associated with the session. Usage is as
-follows::
+:func:`~sqlalchemy.orm.session.Session.merge` transfers state from an
+outside object into a new or already existing instance within a session. It
+also reconciles the incoming data against the state of the
+database, producing a history stream which will be applied towards the next
+flush, or alternatively can be made to produce a simple "transfer" of
+state without producing change history or accessing the database. Usage is as follows::
merged_object = session.merge(existing_object)
When given an instance, it follows these steps:
* It examines the primary key of the instance. If it's present, it attempts
- to load an instance with that primary key (or pulls from the local
- identity map).
-* If there's no primary key on the given instance, or the given primary key
- does not exist in the database, a new instance is created.
+ to locate that instance in the local identity map. If the ``load=True``
+ flag is left at its default, it also checks the database for this primary
+ key if not located locally.
+* If the given instance has no primary key, or if no instance can be found
+ with the primary key given, a new instance is created.
* The state of the given instance is then copied onto the located/newly
- created instance.
-* The operation is cascaded to associated child items along the ``merge``
- cascade. Note that all changes present on the given instance, including
- changes to collections, are merged.
+ created instance. For attributes which are present on the source
+ instance, the value is transferred to the target instance. For mapped
+ attributes which aren't present on the source, the attribute is
+ expired on the target instance, discarding its existing value.
+
+ If the ``load=True`` flag is left at its default,
+ this copy process emits events and will load the target object's
+ unloaded collections for each attribute present on the source object,
+ so that the incoming state can be reconciled against what's
+ present in the database. If ``load``
+ is passed as ``False``, the incoming data is "stamped" directly without
+ producing any history.
+* The operation is cascaded to related objects and collections, as
+ indicated by the ``merge`` cascade (see :ref:`unitofwork_cascades`).
* The new instance is returned.
-With :func:`~sqlalchemy.orm.session.Session.merge`, the given instance is not
-placed within the session, and can be associated with a different session or
-detached. :func:`~sqlalchemy.orm.session.Session.merge` is very useful for
+With :meth:`~.Session.merge`, the given "source"
+instance is not modifed nor is it associated with the target :class:`.Session`,
+and remains available to be merged with any number of other :class:`.Session`
+objects. :meth:`~.Session.merge` is useful for
taking the state of any kind of object structure without regard for its
-origins or current session associations and placing that state within a
-session. Here's two examples:
+origins or current session associations and copying its state into a
+new session. Here's some examples:
-* An application wants to transfer the state of a series of objects
- into a :class:`.Session` maintained by a worker thread or other
- concurrent system. :meth:`~.Session.merge` makes a copy of each object
- to be placed into this new :class:`.Session`. At the end of the operation,
- the parent thread/process maintains the objects it started with,
- and the thread/worker can proceed with local copies of those objects.
+* An application which reads an object structure from a file and wishes to
+ save it to the database might parse the file, build up the
+ structure, and then use
+ :meth:`~.Session.merge` to save it
+ to the database, ensuring that the data within the file is
+ used to formulate the primary key of each element of the
+ structure. Later, when the file has changed, the same
+ process can be re-run, producing a slightly different
+ object structure, which can then be ``merged`` in again,
+ and the :class:`~sqlalchemy.orm.session.Session` will
+ automatically update the database to reflect those
+ changes, loading each object from the database by primary key and
+ then updating its state with the new state given.
* An application is storing objects in an in-memory cache, shared by
many :class:`.Session` objects simultaneously. :meth:`~.Session.merge`
@@ -442,19 +463,16 @@ session. Here's two examples:
that was designed to work with cache-extended :class:`.Query`
objects - see the section :ref:`examples_caching`.
-* An application which reads an object structure from a file and wishes to
- save it to the database might parse the file, build up the
- structure, and then use
- :meth:`~.Session.merge` to save it
- to the database, ensuring that the data within the file is
- used to formulate the primary key of each element of the
- structure. Later, when the file has changed, the same
- process can be re-run, producing a slightly different
- object structure, which can then be ``merged`` in again,
- and the :class:`~sqlalchemy.orm.session.Session` will
- automatically update the database to reflect those
- changes.
+* An application wants to transfer the state of a series of objects
+ into a :class:`.Session` maintained by a worker thread or other
+ concurrent system. :meth:`~.Session.merge` makes a copy of each object
+ to be placed into this new :class:`.Session`. At the end of the operation,
+ the parent thread/process maintains the objects it started with,
+ and the thread/worker can proceed with local copies of those objects.
+ In the "transfer between threads/processes" use case, the application
+ may want to use the ``load=False`` flag as well to avoid overhead and
+ redundant SQL queries as the data is transferred.
Merge Tips
~~~~~~~~~~
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index e665fbdb6..2bd765258 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -1315,14 +1315,18 @@ class Session(object):
self._delete_impl(st_)
def merge(self, instance, load=True):
- """Copy the state an instance onto the persistent instance with the
- same identifier.
-
- If there is no persistent instance currently associated with the
- session, it will be loaded. Return the persistent instance. If the
- given instance is unsaved, save a copy of and return it as a newly
- persistent instance. The given instance does not become associated
- with the session.
+ """Copy the state of a given instance into a corresponding instance
+ within this :class:`.Session`.
+
+ :meth:`.Session.merge` examines the primary key attributes of the
+ source instance, and attempts to reconcile it with an instance of the
+ same primary key in the session. If not found locally, it attempts
+ to load the object from the database based on primary key, and if
+ none can be located, creates a new instance. The state of each attribute
+ on the source instance is then copied to the target instance.
+ The resulting target instance is then returned by the method; the
+ original source instance is left unmodified, and un-associated with the
+ :class:`.Session` if not already.
This operation cascades to associated instances if the association is
mapped with ``cascade="merge"``.
@@ -1331,26 +1335,26 @@ class Session(object):
:param instance: Instance to be merged.
:param load: Boolean, when False, :meth:`.merge` switches into
- a "high performance" mode which causes it to skip all database
- access. The state of the given object is transferred directly
- into the :class:`.Session` without checking the database
- for existing data or discrepancies. This flag is used for
+ a "high performance" mode which causes it to forego emitting history
+ events as well as all database access. This flag is used for
cases such as transferring graphs of objects into a :class:`.Session`
from a second level cache, or to transfer just-loaded objects
into the :class:`.Session` owned by a worker thread or process
without re-querying the database.
The ``load=False`` use case adds the caveat that the given
- object has to be in a "clean" state. This is so that when
- the merge operation cascades onto related objects and
- collections, the related values can be "stamped" onto the
- target object as is, without concern for reconciling their
- contents with any existing database value. While there's no technical
- reason the state of the object can't be taken as is whether or
- not it's dirty, it suggests a mis-use of the method, as state which
- wasn't pulled from the database originally can't reliably be passed back to
- the database without knowing the database's current state.
-
+ object has to be in a "clean" state, that is, has no pending changes
+ to be flushed - even if the incoming object is detached from any
+ :class:`.Session`. This is so that when
+ the merge operation populates local attributes and
+ cascades to related objects and
+ collections, the values can be "stamped" onto the
+ target object as is, without generating any history or attribute
+ events, and without the need to reconcile the incoming data with
+ any existing related objects or collections that might not
+ be loaded. The resulting objects from ``load=False`` are always
+ produced as "clean", so it is only appropriate that the given objects
+ should be "clean" as well, else this suggests a mis-use of the method.
"""