]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- fix up query update /delete documentation, make warnings
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 10 Dec 2014 18:08:53 +0000 (13:08 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 10 Dec 2014 18:10:53 +0000 (13:10 -0500)
a lot clearer, partial fixes for #3252

(cherry picked from commit 3c70f609507ccc6775495cc533265aeb645528cd)

Conflicts:
lib/sqlalchemy/orm/query.py

lib/sqlalchemy/orm/query.py

index 0ebf9dd50a46e4128c52f6dbd3f65e725e7ab812..7a5cd5f2c2469989a402f46905c6374c166f8538 100644 (file)
@@ -2647,6 +2647,18 @@ class Query(object):
 
         Deletes rows matched by this query from the database.
 
+        E.g.::
+
+            sess.query(User).filter(User.age == 25).\\
+                delete(synchronize_session=False)
+
+            sess.query(User).filter(User.age == 25).\\
+                delete(synchronize_session='evaluate')
+
+        .. warning:: The :meth:`.Query.delete` method is a "bulk" operation,
+           which bypasses ORM unit-of-work automation in favor of greater
+           performance.  **Please read all caveats and warnings below.**
+
         :param synchronize_session: chooses the strategy for the removal of
             matched objects from the session. Valid values are:
 
@@ -2665,8 +2677,7 @@ class Query(object):
 
             ``'evaluate'`` - Evaluate the query's criteria in Python straight
             on the objects in the session. If evaluation of the criteria isn't
-            implemented, an error is raised.  In that case you probably
-            want to use the 'fetch' strategy as a fallback.
+            implemented, an error is raised.
 
             The expression evaluator currently doesn't account for differing
             string collations between the database and Python.
@@ -2674,29 +2685,42 @@ class Query(object):
         :return: the count of rows matched as returned by the database's
           "row count" feature.
 
-        This method has several key caveats:
-
-        * The method does **not** offer in-Python cascading of relationships
-          - it is assumed that ON DELETE CASCADE/SET NULL/etc. is configured
-          for any foreign key references which require it, otherwise the
-          database may emit an integrity violation if foreign key references
-          are being enforced.
-
-          After the DELETE, dependent objects in the :class:`.Session` which
-          were impacted by an ON DELETE may not contain the current
-          state, or may have been deleted. This issue is resolved once the
-          :class:`.Session` is expired,
-          which normally occurs upon :meth:`.Session.commit` or can be forced
-          by using :meth:`.Session.expire_all`.  Accessing an expired object
-          whose row has been deleted will invoke a SELECT to locate the
-          row; when the row is not found, an
-          :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised.
-
-        * The :meth:`.MapperEvents.before_delete` and
-          :meth:`.MapperEvents.after_delete`
-          events are **not** invoked from this method.  Instead, the
-          :meth:`.SessionEvents.after_bulk_delete` method is provided to act
-          upon a mass DELETE of entity rows.
+        .. warning:: **Additional Caveats for bulk query deletes**
+
+            * The method does **not** offer in-Python cascading of
+              relationships - it is assumed that ON DELETE CASCADE/SET
+              NULL/etc. is configured for any foreign key references
+              which require it, otherwise the database may emit an
+              integrity violation if foreign key references are being
+              enforced.
+
+              After the DELETE, dependent objects in the
+              :class:`.Session` which were impacted by an ON DELETE
+              may not contain the current state, or may have been
+              deleted. This issue is resolved once the
+              :class:`.Session` is expired, which normally occurs upon
+              :meth:`.Session.commit` or can be forced by using
+              :meth:`.Session.expire_all`.  Accessing an expired
+              object whose row has been deleted will invoke a SELECT
+              to locate the row; when the row is not found, an
+              :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is
+              raised.
+
+            * The ``'fetch'`` strategy results in an additional
+              SELECT statement emitted and will significantly reduce
+              performance.
+
+            * The ``'evaulate'`` strategy performs a scan of
+              all matching objects within the :class:`.Session`; if the
+              contents of the :class:`.Session` are expired, such as
+              via a proceeding :meth:`.Session.commit` call, **this will
+              result in SELECT queries emitted for every matching object**.
+
+            * The :meth:`.MapperEvents.before_delete` and
+              :meth:`.MapperEvents.after_delete`
+              events **are not invoked** from this method.  Instead, the
+              :meth:`.SessionEvents.after_bulk_delete` method is provided to
+              act upon a mass DELETE of entity rows.
 
         .. seealso::
 
@@ -2717,11 +2741,26 @@ class Query(object):
 
         Updates rows matched by this query in the database.
 
-        :param values: a dictionary with attributes names as keys and literal
-          values or sql expressions as values.
+        E.g.::
+
+            sess.query(User).filter(User.age == 25).\\
+                update({User.age: User.age - 10}, synchronize_session=False)
+
+            sess.query(User).filter(User.age == 25).\\
+                update({"age": User.age - 10}, synchronize_session='evaluate')
+
+
+        .. warning:: The :meth:`.Query.update` method is a "bulk" operation,
+           which bypasses ORM unit-of-work automation in favor of greater
+           performance.  **Please read all caveats and warnings below.**
+
+
+        :param values: a dictionary with attributes names, or alternatively
+         mapped attributes or SQL expressions, as keys, and literal
+         values or sql expressions as values.
 
         :param synchronize_session: chooses the strategy to update the
-            attributes on objects in the session. Valid values are:
+         attributes on objects in the session. Valid values are:
 
             ``False`` - don't synchronize the session. This option is the most
             efficient and is reliable once the session is expired, which
@@ -2742,43 +2781,56 @@ class Query(object):
             string collations between the database and Python.
 
         :return: the count of rows matched as returned by the database's
-          "row count" feature.
-
-        This method has several key caveats:
-
-        * The method does **not** offer in-Python cascading of relationships
-          - it is assumed that ON UPDATE CASCADE is configured for any foreign
-          key references which require it, otherwise the database may emit an
-          integrity violation if foreign key references are being enforced.
-
-          After the UPDATE, dependent objects in the :class:`.Session` which
-          were impacted by an ON UPDATE CASCADE may not contain the current
-          state; this issue is resolved once the :class:`.Session` is expired,
-          which normally occurs upon :meth:`.Session.commit` or can be forced
-          by using :meth:`.Session.expire_all`.
-
-        * As of 0.8, this method will support multiple table updates, as
-          detailed in :ref:`multi_table_updates`, and this behavior does
-          extend to support updates of joined-inheritance and other multiple
-          table mappings.  However, the **join condition of an inheritance
-          mapper is currently not automatically rendered**.
-          Care must be taken in any multiple-table update to explicitly
-          include the joining condition between those tables, even in mappings
-          where this is normally automatic.
-          E.g. if a class ``Engineer`` subclasses ``Employee``, an UPDATE of
-          the ``Engineer`` local table using criteria against the ``Employee``
-          local table might look like::
-
-                session.query(Engineer).\\
-                    filter(Engineer.id == Employee.id).\\
-                    filter(Employee.name == 'dilbert').\\
-                    update({"engineer_type": "programmer"})
-
-        * The :meth:`.MapperEvents.before_update` and
-          :meth:`.MapperEvents.after_update`
-          events are **not** invoked from this method.  Instead, the
-          :meth:`.SessionEvents.after_bulk_update` method is provided to act
-          upon a mass UPDATE of entity rows.
+         "row count" feature.
+
+        .. warning:: **Additional Caveats for bulk query updates**
+
+            * The method does **not** offer in-Python cascading of
+              relationships - it is assumed that ON UPDATE CASCADE is
+              configured for any foreign key references which require
+              it, otherwise the database may emit an integrity
+              violation if foreign key references are being enforced.
+
+              After the UPDATE, dependent objects in the
+              :class:`.Session` which were impacted by an ON UPDATE
+              CASCADE may not contain the current state; this issue is
+              resolved once the :class:`.Session` is expired, which
+              normally occurs upon :meth:`.Session.commit` or can be
+              forced by using :meth:`.Session.expire_all`.
+
+            * The ``'fetch'`` strategy results in an additional
+              SELECT statement emitted and will significantly reduce
+              performance.
+
+            * The ``'evaulate'`` strategy performs a scan of
+              all matching objects within the :class:`.Session`; if the
+              contents of the :class:`.Session` are expired, such as
+              via a proceeding :meth:`.Session.commit` call, **this will
+              result in SELECT queries emitted for every matching object**.
+
+            * The method supports multiple table updates, as detailed
+              in :ref:`multi_table_updates`, and this behavior does
+              extend to support updates of joined-inheritance and
+              other multiple table mappings.  However, the **join
+              condition of an inheritance mapper is not
+              automatically rendered**. Care must be taken in any
+              multiple-table update to explicitly include the joining
+              condition between those tables, even in mappings where
+              this is normally automatic. E.g. if a class ``Engineer``
+              subclasses ``Employee``, an UPDATE of the ``Engineer``
+              local table using criteria against the ``Employee``
+              local table might look like::
+
+                    session.query(Engineer).\\
+                        filter(Engineer.id == Employee.id).\\
+                        filter(Employee.name == 'dilbert').\\
+                        update({"engineer_type": "programmer"})
+
+            * The :meth:`.MapperEvents.before_update` and
+              :meth:`.MapperEvents.after_update`
+              events **are not invoked from this method**.  Instead, the
+              :meth:`.SessionEvents.after_bulk_update` method is provided to
+              act upon a mass UPDATE of entity rows.
 
         .. seealso::