Commits

Mike Bayer committed 65bf4d9 Draft

- continue editing the merge docs

  • Participants
  • Parent commits b63fb2a

Comments (0)

Files changed (2)

doc/build/orm/session.rst

 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`
   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
 ~~~~~~~~~~

lib/sqlalchemy/orm/session.py

             self._delete_impl(st_)
 
     def merge(self, instance, load=True):
-        """Copy the state an instance onto the persistent instance with the
-        same identifier.
+        """Copy the state of a given instance into a corresponding instance
+        within this :class:`.Session`.
 
-        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.
+        :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"``.
 
         :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.
 
         """