]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Improve ResultProxy/Row documentation before the API change
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 13 Feb 2020 17:48:26 +0000 (12:48 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 13 Feb 2020 18:01:27 +0000 (13:01 -0500)
We'd like to merge Ieb9085e9bcff564359095b754da9ae0af55679f0,
however the documentation in that change should be based off
of more comprehensive documentation than what we have already.

Add the notion of "row" to the tutorial and document all
methods.   This will also be backported at least to 1.3
in terms of RowProxy.

Change-Id: I2173aecc86cf15c5a7c473b8f471639ee9082f84
(cherry picked from commit 8fabe50d7a47b50215a7ea4cf1d39409d9529e51)
(cherry picked from commit c5d51df5e4fe329913ad4c04ed7db8e1c61c3b84)

doc/build/core/connections.rst
doc/build/core/tutorial.rst
lib/sqlalchemy/engine/result.py

index b9b44b011ad42368a0705e109a47defc8254d561..269bc0ff5e406bcf76780c72180c8406d00ac7c5 100644 (file)
@@ -732,7 +732,6 @@ Connection / Engine API
 
 .. autoclass:: ResultProxy
     :members:
-    :private-members: _soft_close
 
 .. autoclass:: RowProxy
     :members:
index fc76bebdbc6666eea50879b9a4837787d994ad2c..eb9c2ada61f485b2f95d7b8c8c870b8cfdadff86 100644 (file)
@@ -395,8 +395,10 @@ generated a FROM clause for us. The result returned is again a
 :class:`~sqlalchemy.engine.ResultProxy` object, which acts much like a
 DBAPI cursor, including methods such as
 :func:`~sqlalchemy.engine.ResultProxy.fetchone` and
-:func:`~sqlalchemy.engine.ResultProxy.fetchall`. The easiest way to get
-rows from it is to just iterate:
+:func:`~sqlalchemy.engine.ResultProxy.fetchall`.    These methods return
+row objects, which are provided via the :class:`.RowProxy` class.  The
+result object can be iterated directly in order to provide an iterator
+of :class:`.RowProxy` objects:
 
 .. sourcecode:: pycon+sql
 
@@ -405,9 +407,11 @@ rows from it is to just iterate:
     (1, u'jack', u'Jack Jones')
     (2, u'wendy', u'Wendy Williams')
 
-Above, we see that printing each row produces a simple tuple-like result. We
-have more options at accessing the data in each row. One very common way is
-through dictionary access, using the string names of columns:
+Above, we see that printing each :class:`.RowProxy` produces a simple
+tuple-like result.  The :class:`.RowProxy` behaves like a hybrid between
+a Python mapping and tuple, with several methods of retrieving the data
+in each column.  One common way is
+as a Python mapping of strings, using the string names of columns:
 
 .. sourcecode:: pycon+sql
 
@@ -420,7 +424,7 @@ through dictionary access, using the string names of columns:
     >>> print("name:", row['name'], "; fullname:", row['fullname'])
     name: jack ; fullname: Jack Jones
 
-Integer indexes work as well:
+Another way is as a Python sequence, using integer indexes:
 
 .. sourcecode:: pycon+sql
 
@@ -428,8 +432,10 @@ Integer indexes work as well:
     >>> print("name:", row[1], "; fullname:", row[2])
     name: wendy ; fullname: Wendy Williams
 
-But another way, whose usefulness will become apparent later on, is to use the
-:class:`~sqlalchemy.schema.Column` objects directly as keys:
+A more specialized method of column access is to use the SQL construct that
+directly corresponds to a particular column as the mapping key; in this
+example, it means we would use the  :class:`.Column` objects selected in our
+SELECT directly as keys:
 
 .. sourcecode:: pycon+sql
 
@@ -441,17 +447,19 @@ But another way, whose usefulness will become apparent later on, is to use the
     {stop}name: jack ; fullname: Jack Jones
     name: wendy ; fullname: Wendy Williams
 
-Result sets which have pending rows remaining should be explicitly closed
-before discarding. While the cursor and connection resources referenced by the
-:class:`~sqlalchemy.engine.ResultProxy` will be respectively closed and
-returned to the connection pool when the object is garbage collected, it's
-better to make it explicit as some database APIs are very picky about such
-things:
+The :class:`.ResultProxy` object features "auto-close" behavior that closes the
+underlying DBAPI ``cursor`` object when all pending result rows have been
+fetched.   If a :class:`.ResultProxy` is to be discarded before such an
+autoclose has occurred, it can be explicitly closed using the
+:meth:`.ResultProxy.close` method:
 
 .. sourcecode:: pycon+sql
 
     >>> result.close()
 
+Selecting Specific Columns
+===========================
+
 If we'd like to more carefully control the columns which are placed in the
 COLUMNS clause of the select, we reference individual
 :class:`~sqlalchemy.schema.Column` objects from our
index f632132022fdfae6aa2351ebc60e896d7c4a7089..e72562d2a60000a31d89030d92bcc04344810a04 100644 (file)
@@ -115,13 +115,20 @@ except ImportError:
 
 
 class RowProxy(BaseRowProxy):
-    """Proxy values from a single cursor row.
+    """Represent a single result row.
+
+    The :class:`.RowProxy` object is retrieved from a database result, from the
+    :class:`.ResultProxy` object using methods like
+    :meth:`.ResultProxy.fetchall`.
+
+    The :class:`.RowProxy` object seeks to act mostly like a Python named
+    tuple, but also provides some Python dictionary behaviors at the same time.
+
+    .. seealso::
+
+        :ref:`coretutorial_selecting` - includes examples of selecting
+        rows from SELECT statements.
 
-    Mostly follows "ordered dictionary" behavior, mapping result
-    values to the string-based column name, the integer position of
-    the result in the row, as well as Column instances which can be
-    mapped to the original Columns that produced this result set (for
-    results that correspond to constructed SQL expressions).
     """
 
     __slots__ = ()
@@ -169,26 +176,73 @@ class RowProxy(BaseRowProxy):
         return repr(sql_util._repr_row(self))
 
     def has_key(self, key):
-        """Return True if this RowProxy contains the given key."""
+        """Return True if this :class:`.RowProxy` contains the given key.
+
+        Through the SQLAlchemy 1.x series, the ``__contains__()`` method
+        of :class:`.RowProxy` also links to :meth:`.RowProxy.has_key`, in that
+        an expression such as ::
+
+            "some_col" in row
+
+        Will return True if the row contains a column named ``"some_col"``,
+        in the way that a Python mapping works.
+
+        However, it is planned that the 2.0 series of SQLAlchemy will reverse
+        this behavior so that ``__contains__()`` will refer to a value being
+        present in the row, in the way that a Python tuple works.
+
+        """
 
         return self._parent._has_key(key)
 
     def items(self):
-        """Return a list of tuples, each tuple containing a key/value pair."""
-        # TODO: no coverage here
+        """Return a list of tuples, each tuple containing a key/value pair.
+
+        This method is analogous to the Python dictionary ``.items()`` method,
+        except that it returns a list, not an iterator.
+
+        """
+
         return [(key, self[key]) for key in self.keys()]
 
     def keys(self):
-        """Return the list of keys as strings represented by this RowProxy."""
+        """Return the list of keys as strings represented by this
+        :class:`.RowProxy`.
+
+        This method is analogous to the Python dictionary ``.keys()`` method,
+        except that it returns a list, not an iterator.
+
+        """
 
         return self._parent.keys
 
     def iterkeys(self):
+        """Return a an iterator against the :meth:`.RowProxy.keys` method.
+
+        This method is analogous to the Python-2-only dictionary
+        ``.iterkeys()`` method.
+
+        """
         return iter(self._parent.keys)
 
     def itervalues(self):
+        """Return a an iterator against the :meth:`.RowProxy.values` method.
+
+        This method is analogous to the Python-2-only dictionary
+        ``.itervalues()`` method.
+
+        """
         return iter(self)
 
+    def values(self):
+        """Return the values represented by this :class:`.RowProxy` as a list.
+
+        This method is analogous to the Python dictionary ``.values()`` method,
+        except that it returns a list, not an iterator.
+
+        """
+        return super(RowProxy, self).values()
+
 
 try:
     # Register RowProxy with Sequence,
@@ -684,23 +738,16 @@ class ResultMetaData(object):
 
 
 class ResultProxy(object):
-    """Wraps a DB-API cursor object to provide easier access to row columns.
-
-    Individual columns may be accessed by their integer position,
-    case-insensitive column name, or by ``schema.Column``
-    object. e.g.::
-
-      row = fetchone()
+    """A facade around a DBAPI cursor object.
 
-      col1 = row[0]    # access via integer position
+    Returns database rows via the :class:`.RowProxy` class, which provides
+    additional API features and behaviors on top of the raw data returned
+    by the DBAPI.
 
-      col2 = row['col2']   # access via name
-
-      col3 = row[mytable.c.mycol] # access via Column object.
+    .. seealso::
 
-    ``ResultProxy`` also handles post-processing of result column
-    data using ``TypeEngine`` objects, which are referenced from
-    the originating SQL statement that produced this result set.
+        :ref:`coretutorial_selecting` - introductory material for accessing
+        :class:`.ResultProxy` and :class:`.RowProxy` objects.
 
     """
 
@@ -758,7 +805,9 @@ class ResultProxy(object):
                 )
 
     def keys(self):
-        """Return the current set of string keys for rows."""
+        """Return the list of string keys that would represented by each
+        :class:`.RowProxy`."""
+
         if self._metadata:
             return self._metadata.keys
         else:
@@ -931,8 +980,6 @@ class ResultProxy(object):
 
             :ref:`connections_toplevel`
 
-            :meth:`.ResultProxy._soft_close`
-
         """
 
         if not self.closed:
@@ -950,7 +997,10 @@ class ResultProxy(object):
                 yield row
 
     def __next__(self):
-        """Implement the next() protocol.
+        """Implement the Python next() protocol.
+
+        This method, mirrored as both ``.next()`` and  ``.__next__()``, is part
+        of Python's API for producing iterator-like behavior.
 
         .. versionadded:: 1.2
 
@@ -1203,9 +1253,7 @@ class ResultProxy(object):
         an empty list.   After the :meth:`.ResultProxy.close` method is
         called, the method will raise :class:`.ResourceClosedError`.
 
-        .. versionchanged:: 1.0.0 - Added "soft close" behavior which
-           allows the result to be used in an "exhausted" state prior to
-           calling the :meth:`.ResultProxy.close` method.
+        :return: a list of :class:`.RowProxy` objects
 
         """
 
@@ -1231,9 +1279,7 @@ class ResultProxy(object):
         an empty list.   After the :meth:`.ResultProxy.close` method is
         called, the method will raise :class:`.ResourceClosedError`.
 
-        .. versionchanged:: 1.0.0 - Added "soft close" behavior which
-           allows the result to be used in an "exhausted" state prior to
-           calling the :meth:`.ResultProxy.close` method.
+        :return: a list of :class:`.RowProxy` objects
 
         """
 
@@ -1259,9 +1305,7 @@ class ResultProxy(object):
         After the :meth:`.ResultProxy.close` method is
         called, the method will raise :class:`.ResourceClosedError`.
 
-        .. versionchanged:: 1.0.0 - Added "soft close" behavior which
-           allows the result to be used in an "exhausted" state prior to
-           calling the :meth:`.ResultProxy.close` method.
+        :return: a :class:`.RowProxy` object, or None if no rows remain
 
         """
         try:
@@ -1279,11 +1323,11 @@ class ResultProxy(object):
     def first(self):
         """Fetch the first row and then close the result set unconditionally.
 
-        Returns None if no row is present.
-
         After calling this method, the object is fully closed,
         e.g. the :meth:`.ResultProxy.close` method will have been called.
 
+        :return: a :class:`.RowProxy` object, or None if no rows remain
+
         """
         if self._metadata is None:
             return self._non_result(None)
@@ -1306,11 +1350,11 @@ class ResultProxy(object):
     def scalar(self):
         """Fetch the first column of the first row, and close the result set.
 
-        Returns None if no row is present.
-
         After calling this method, the object is fully closed,
         e.g. the :meth:`.ResultProxy.close` method will have been called.
 
+        :return: a Python scalar value , or None if no rows remain
+
         """
         row = self.first()
         if row is not None: