Skip to content

Commit aabe831

Browse files
miss-islingtonerlend-aaslandCAM-Gerlachezio-melotti
authored
gh-96168: Add sqlite3 row factory how-to (GH-99507)
(cherry picked from commit 8749121) Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com> Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM> Co-authored-by: Ezio Melotti <ezio.melotti@gmail.com>
1 parent c426815 commit aabe831

File tree

1 file changed

+119
-41
lines changed

1 file changed

+119
-41
lines changed

Doc/library/sqlite3.rst

Lines changed: 119 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ inserted data and retrieved values from it in multiple ways.
239239
* :ref:`sqlite3-adapters`
240240
* :ref:`sqlite3-converters`
241241
* :ref:`sqlite3-connection-context-manager`
242+
* :ref:`sqlite3-howto-row-factory`
242243

243244
* :ref:`sqlite3-explanation` for in-depth background on transaction control.
244245

@@ -946,31 +947,14 @@ Connection objects
946947

947948
.. attribute:: row_factory
948949

949-
A callable that accepts two arguments,
950-
a :class:`Cursor` object and the raw row results as a :class:`tuple`,
951-
and returns a custom object representing an SQLite row.
952-
953-
Example:
950+
The initial :attr:`~Cursor.row_factory`
951+
for :class:`Cursor` objects created from this connection.
952+
Assigning to this attribute does not affect the :attr:`!row_factory`
953+
of existing cursors belonging to this connection, only new ones.
954+
Is ``None`` by default,
955+
meaning each row is returned as a :class:`tuple`.
954956

955-
.. doctest::
956-
957-
>>> def dict_factory(cursor, row):
958-
... col_names = [col[0] for col in cursor.description]
959-
... return {key: value for key, value in zip(col_names, row)}
960-
>>> con = sqlite3.connect(":memory:")
961-
>>> con.row_factory = dict_factory
962-
>>> for row in con.execute("SELECT 1 AS a, 2 AS b"):
963-
... print(row)
964-
{'a': 1, 'b': 2}
965-
966-
If returning a tuple doesn't suffice and you want name-based access to
967-
columns, you should consider setting :attr:`row_factory` to the
968-
highly optimized :class:`sqlite3.Row` type. :class:`Row` provides both
969-
index-based and case-insensitive name-based access to columns with almost no
970-
memory overhead. It will probably be better than your own custom
971-
dictionary-based approach or even a db_row based solution.
972-
973-
.. XXX what's a db_row-based solution?
957+
See :ref:`sqlite3-howto-row-factory` for more details.
974958

975959
.. attribute:: text_factory
976960

@@ -1122,7 +1106,7 @@ Cursor objects
11221106

11231107
.. method:: fetchone()
11241108

1125-
If :attr:`~Connection.row_factory` is ``None``,
1109+
If :attr:`~Cursor.row_factory` is ``None``,
11261110
return the next row query result set as a :class:`tuple`.
11271111
Else, pass it to the row factory and return its result.
11281112
Return ``None`` if no more data is available.
@@ -1216,6 +1200,22 @@ Cursor objects
12161200
including :abbr:`CTE (Common Table Expression)` queries.
12171201
It is only updated by the :meth:`execute` and :meth:`executemany` methods.
12181202

1203+
.. attribute:: row_factory
1204+
1205+
Control how a row fetched from this :class:`!Cursor` is represented.
1206+
If ``None``, a row is represented as a :class:`tuple`.
1207+
Can be set to the included :class:`sqlite3.Row`;
1208+
or a :term:`callable` that accepts two arguments,
1209+
a :class:`Cursor` object and the :class:`!tuple` of row values,
1210+
and returns a custom object representing an SQLite row.
1211+
1212+
Defaults to what :attr:`Connection.row_factory` was set to
1213+
when the :class:`!Cursor` was created.
1214+
Assigning to this attribute does not affect
1215+
:attr:`Connection.row_factory` of the parent connection.
1216+
1217+
See :ref:`sqlite3-howto-row-factory` for more details.
1218+
12191219

12201220
.. The sqlite3.Row example used to be a how-to. It has now been incorporated
12211221
into the Row reference. We keep the anchor here in order not to break
@@ -1234,7 +1234,10 @@ Row objects
12341234
It supports iteration, equality testing, :func:`len`,
12351235
and :term:`mapping` access by column name and index.
12361236

1237-
Two row objects compare equal if have equal columns and equal members.
1237+
Two :class:`!Row` objects compare equal
1238+
if they have identical column names and values.
1239+
1240+
See :ref:`sqlite3-howto-row-factory` for more details.
12381241

12391242
.. method:: keys
12401243

@@ -1245,21 +1248,6 @@ Row objects
12451248
.. versionchanged:: 3.5
12461249
Added support of slicing.
12471250

1248-
Example:
1249-
1250-
.. doctest::
1251-
1252-
>>> con = sqlite3.connect(":memory:")
1253-
>>> con.row_factory = sqlite3.Row
1254-
>>> res = con.execute("SELECT 'Earth' AS name, 6378 AS radius")
1255-
>>> row = res.fetchone()
1256-
>>> row.keys()
1257-
['name', 'radius']
1258-
>>> row[0], row["name"] # Access by index and name.
1259-
('Earth', 'Earth')
1260-
>>> row["RADIUS"] # Column names are case-insensitive.
1261-
6378
1262-
12631251

12641252
PrepareProtocol objects
12651253
^^^^^^^^^^^^^^^^^^^^^^^
@@ -1855,6 +1843,96 @@ can be found in the `SQLite URI documentation`_.
18551843
.. _SQLite URI documentation: https://www.sqlite.org/uri.html
18561844

18571845

1846+
.. _sqlite3-howto-row-factory:
1847+
1848+
How to create and use row factories
1849+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1850+
1851+
By default, :mod:`!sqlite3` represents each row as a :class:`tuple`.
1852+
If a :class:`!tuple` does not suit your needs,
1853+
you can use the :class:`sqlite3.Row` class
1854+
or a custom :attr:`~Cursor.row_factory`.
1855+
1856+
While :attr:`!row_factory` exists as an attribute both on the
1857+
:class:`Cursor` and the :class:`Connection`,
1858+
it is recommended to set :class:`Connection.row_factory`,
1859+
so all cursors created from the connection will use the same row factory.
1860+
1861+
:class:`!Row` provides indexed and case-insensitive named access to columns,
1862+
with minimal memory overhead and performance impact over a :class:`!tuple`.
1863+
To use :class:`!Row` as a row factory,
1864+
assign it to the :attr:`!row_factory` attribute:
1865+
1866+
.. doctest::
1867+
1868+
>>> con = sqlite3.connect(":memory:")
1869+
>>> con.row_factory = sqlite3.Row
1870+
1871+
Queries now return :class:`!Row` objects:
1872+
1873+
.. doctest::
1874+
1875+
>>> res = con.execute("SELECT 'Earth' AS name, 6378 AS radius")
1876+
>>> row = res.fetchone()
1877+
>>> row.keys()
1878+
['name', 'radius']
1879+
>>> row[0] # Access by index.
1880+
'Earth'
1881+
>>> row["name"] # Access by name.
1882+
'Earth'
1883+
>>> row["RADIUS"] # Column names are case-insensitive.
1884+
6378
1885+
1886+
You can create a custom :attr:`~Cursor.row_factory`
1887+
that returns each row as a :class:`dict`, with column names mapped to values:
1888+
1889+
.. testcode::
1890+
1891+
def dict_factory(cursor, row):
1892+
fields = [column[0] for column in cursor.description]
1893+
return {key: value for key, value in zip(fields, row)}
1894+
1895+
Using it, queries now return a :class:`!dict` instead of a :class:`!tuple`:
1896+
1897+
.. doctest::
1898+
1899+
>>> con = sqlite3.connect(":memory:")
1900+
>>> con.row_factory = dict_factory
1901+
>>> for row in con.execute("SELECT 1 AS a, 2 AS b"):
1902+
... print(row)
1903+
{'a': 1, 'b': 2}
1904+
1905+
The following row factory returns a :term:`named tuple`:
1906+
1907+
.. testcode::
1908+
1909+
from collections import namedtuple
1910+
1911+
def namedtuple_factory(cursor, row):
1912+
fields = [column[0] for column in cursor.description]
1913+
cls = namedtuple("Row", fields)
1914+
return cls._make(row)
1915+
1916+
:func:`!namedtuple_factory` can be used as follows:
1917+
1918+
.. doctest::
1919+
1920+
>>> con = sqlite3.connect(":memory:")
1921+
>>> con.row_factory = namedtuple_factory
1922+
>>> cur = con.execute("SELECT 1 AS a, 2 AS b")
1923+
>>> row = cur.fetchone()
1924+
>>> row
1925+
Row(a=1, b=2)
1926+
>>> row[0] # Indexed access.
1927+
1
1928+
>>> row.b # Attribute access.
1929+
2
1930+
1931+
With some adjustments, the above recipe can be adapted to use a
1932+
:class:`~dataclasses.dataclass`, or any other custom class,
1933+
instead of a :class:`~collections.namedtuple`.
1934+
1935+
18581936
.. _sqlite3-explanation:
18591937

18601938
Explanation

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy