Skip to content

Commit 5b74084

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 <[email protected]> Co-authored-by: C.A.M. Gerlach <[email protected]> Co-authored-by: Ezio Melotti <[email protected]>
1 parent d279f38 commit 5b74084

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

@@ -1236,31 +1237,14 @@ Connection objects
12361237

12371238
.. attribute:: row_factory
12381239

1239-
A callable that accepts two arguments,
1240-
a :class:`Cursor` object and the raw row results as a :class:`tuple`,
1241-
and returns a custom object representing an SQLite row.
1242-
1243-
Example:
1240+
The initial :attr:`~Cursor.row_factory`
1241+
for :class:`Cursor` objects created from this connection.
1242+
Assigning to this attribute does not affect the :attr:`!row_factory`
1243+
of existing cursors belonging to this connection, only new ones.
1244+
Is ``None`` by default,
1245+
meaning each row is returned as a :class:`tuple`.
12441246

1245-
.. doctest::
1246-
1247-
>>> def dict_factory(cursor, row):
1248-
... col_names = [col[0] for col in cursor.description]
1249-
... return {key: value for key, value in zip(col_names, row)}
1250-
>>> con = sqlite3.connect(":memory:")
1251-
>>> con.row_factory = dict_factory
1252-
>>> for row in con.execute("SELECT 1 AS a, 2 AS b"):
1253-
... print(row)
1254-
{'a': 1, 'b': 2}
1255-
1256-
If returning a tuple doesn't suffice and you want name-based access to
1257-
columns, you should consider setting :attr:`row_factory` to the
1258-
highly optimized :class:`sqlite3.Row` type. :class:`Row` provides both
1259-
index-based and case-insensitive name-based access to columns with almost no
1260-
memory overhead. It will probably be better than your own custom
1261-
dictionary-based approach or even a db_row based solution.
1262-
1263-
.. XXX what's a db_row-based solution?
1247+
See :ref:`sqlite3-howto-row-factory` for more details.
12641248

12651249
.. attribute:: text_factory
12661250

@@ -1413,7 +1397,7 @@ Cursor objects
14131397

14141398
.. method:: fetchone()
14151399

1416-
If :attr:`~Connection.row_factory` is ``None``,
1400+
If :attr:`~Cursor.row_factory` is ``None``,
14171401
return the next row query result set as a :class:`tuple`.
14181402
Else, pass it to the row factory and return its result.
14191403
Return ``None`` if no more data is available.
@@ -1507,6 +1491,22 @@ Cursor objects
15071491
including :abbr:`CTE (Common Table Expression)` queries.
15081492
It is only updated by the :meth:`execute` and :meth:`executemany` methods.
15091493

1494+
.. attribute:: row_factory
1495+
1496+
Control how a row fetched from this :class:`!Cursor` is represented.
1497+
If ``None``, a row is represented as a :class:`tuple`.
1498+
Can be set to the included :class:`sqlite3.Row`;
1499+
or a :term:`callable` that accepts two arguments,
1500+
a :class:`Cursor` object and the :class:`!tuple` of row values,
1501+
and returns a custom object representing an SQLite row.
1502+
1503+
Defaults to what :attr:`Connection.row_factory` was set to
1504+
when the :class:`!Cursor` was created.
1505+
Assigning to this attribute does not affect
1506+
:attr:`Connection.row_factory` of the parent connection.
1507+
1508+
See :ref:`sqlite3-howto-row-factory` for more details.
1509+
15101510

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

1528-
Two row objects compare equal if have equal columns and equal members.
1528+
Two :class:`!Row` objects compare equal
1529+
if they have identical column names and values.
1530+
1531+
See :ref:`sqlite3-howto-row-factory` for more details.
15291532

15301533
.. method:: keys
15311534

@@ -1536,21 +1539,6 @@ Row objects
15361539
.. versionchanged:: 3.5
15371540
Added support of slicing.
15381541

1539-
Example:
1540-
1541-
.. doctest::
1542-
1543-
>>> con = sqlite3.connect(":memory:")
1544-
>>> con.row_factory = sqlite3.Row
1545-
>>> res = con.execute("SELECT 'Earth' AS name, 6378 AS radius")
1546-
>>> row = res.fetchone()
1547-
>>> row.keys()
1548-
['name', 'radius']
1549-
>>> row[0], row["name"] # Access by index and name.
1550-
('Earth', 'Earth')
1551-
>>> row["RADIUS"] # Column names are case-insensitive.
1552-
6378
1553-
15541542

15551543
.. _sqlite3-blob-objects:
15561544

@@ -2241,6 +2229,96 @@ can be found in the `SQLite URI documentation`_.
22412229
.. _SQLite URI documentation: https://www.sqlite.org/uri.html
22422230

22432231

2232+
.. _sqlite3-howto-row-factory:
2233+
2234+
How to create and use row factories
2235+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2236+
2237+
By default, :mod:`!sqlite3` represents each row as a :class:`tuple`.
2238+
If a :class:`!tuple` does not suit your needs,
2239+
you can use the :class:`sqlite3.Row` class
2240+
or a custom :attr:`~Cursor.row_factory`.
2241+
2242+
While :attr:`!row_factory` exists as an attribute both on the
2243+
:class:`Cursor` and the :class:`Connection`,
2244+
it is recommended to set :class:`Connection.row_factory`,
2245+
so all cursors created from the connection will use the same row factory.
2246+
2247+
:class:`!Row` provides indexed and case-insensitive named access to columns,
2248+
with minimal memory overhead and performance impact over a :class:`!tuple`.
2249+
To use :class:`!Row` as a row factory,
2250+
assign it to the :attr:`!row_factory` attribute:
2251+
2252+
.. doctest::
2253+
2254+
>>> con = sqlite3.connect(":memory:")
2255+
>>> con.row_factory = sqlite3.Row
2256+
2257+
Queries now return :class:`!Row` objects:
2258+
2259+
.. doctest::
2260+
2261+
>>> res = con.execute("SELECT 'Earth' AS name, 6378 AS radius")
2262+
>>> row = res.fetchone()
2263+
>>> row.keys()
2264+
['name', 'radius']
2265+
>>> row[0] # Access by index.
2266+
'Earth'
2267+
>>> row["name"] # Access by name.
2268+
'Earth'
2269+
>>> row["RADIUS"] # Column names are case-insensitive.
2270+
6378
2271+
2272+
You can create a custom :attr:`~Cursor.row_factory`
2273+
that returns each row as a :class:`dict`, with column names mapped to values:
2274+
2275+
.. testcode::
2276+
2277+
def dict_factory(cursor, row):
2278+
fields = [column[0] for column in cursor.description]
2279+
return {key: value for key, value in zip(fields, row)}
2280+
2281+
Using it, queries now return a :class:`!dict` instead of a :class:`!tuple`:
2282+
2283+
.. doctest::
2284+
2285+
>>> con = sqlite3.connect(":memory:")
2286+
>>> con.row_factory = dict_factory
2287+
>>> for row in con.execute("SELECT 1 AS a, 2 AS b"):
2288+
... print(row)
2289+
{'a': 1, 'b': 2}
2290+
2291+
The following row factory returns a :term:`named tuple`:
2292+
2293+
.. testcode::
2294+
2295+
from collections import namedtuple
2296+
2297+
def namedtuple_factory(cursor, row):
2298+
fields = [column[0] for column in cursor.description]
2299+
cls = namedtuple("Row", fields)
2300+
return cls._make(row)
2301+
2302+
:func:`!namedtuple_factory` can be used as follows:
2303+
2304+
.. doctest::
2305+
2306+
>>> con = sqlite3.connect(":memory:")
2307+
>>> con.row_factory = namedtuple_factory
2308+
>>> cur = con.execute("SELECT 1 AS a, 2 AS b")
2309+
>>> row = cur.fetchone()
2310+
>>> row
2311+
Row(a=1, b=2)
2312+
>>> row[0] # Indexed access.
2313+
1
2314+
>>> row.b # Attribute access.
2315+
2
2316+
2317+
With some adjustments, the above recipe can be adapted to use a
2318+
:class:`~dataclasses.dataclass`, or any other custom class,
2319+
instead of a :class:`~collections.namedtuple`.
2320+
2321+
22442322
.. _sqlite3-explanation:
22452323

22462324
Explanation

0 commit comments

Comments
 (0)