Skip to content

Commit 8749121

Browse files
erlend-aaslandCAM-Gerlachezio-melotti
authored
gh-96168: Add sqlite3 row factory how-to (#99507)
Co-authored-by: C.A.M. Gerlach <CAM.Gerlach@Gerlach.CAM> Co-authored-by: Ezio Melotti <ezio.melotti@gmail.com>
1 parent d386115 commit 8749121

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

@@ -1316,31 +1317,14 @@ Connection objects
13161317

13171318
.. attribute:: row_factory
13181319

1319-
A callable that accepts two arguments,
1320-
a :class:`Cursor` object and the raw row results as a :class:`tuple`,
1321-
and returns a custom object representing an SQLite row.
1322-
1323-
Example:
1320+
The initial :attr:`~Cursor.row_factory`
1321+
for :class:`Cursor` objects created from this connection.
1322+
Assigning to this attribute does not affect the :attr:`!row_factory`
1323+
of existing cursors belonging to this connection, only new ones.
1324+
Is ``None`` by default,
1325+
meaning each row is returned as a :class:`tuple`.
13241326

1325-
.. doctest::
1326-
1327-
>>> def dict_factory(cursor, row):
1328-
... col_names = [col[0] for col in cursor.description]
1329-
... return {key: value for key, value in zip(col_names, row)}
1330-
>>> con = sqlite3.connect(":memory:")
1331-
>>> con.row_factory = dict_factory
1332-
>>> for row in con.execute("SELECT 1 AS a, 2 AS b"):
1333-
... print(row)
1334-
{'a': 1, 'b': 2}
1335-
1336-
If returning a tuple doesn't suffice and you want name-based access to
1337-
columns, you should consider setting :attr:`row_factory` to the
1338-
highly optimized :class:`sqlite3.Row` type. :class:`Row` provides both
1339-
index-based and case-insensitive name-based access to columns with almost no
1340-
memory overhead. It will probably be better than your own custom
1341-
dictionary-based approach or even a db_row based solution.
1342-
1343-
.. XXX what's a db_row-based solution?
1327+
See :ref:`sqlite3-howto-row-factory` for more details.
13441328

13451329
.. attribute:: text_factory
13461330

@@ -1497,7 +1481,7 @@ Cursor objects
14971481

14981482
.. method:: fetchone()
14991483

1500-
If :attr:`~Connection.row_factory` is ``None``,
1484+
If :attr:`~Cursor.row_factory` is ``None``,
15011485
return the next row query result set as a :class:`tuple`.
15021486
Else, pass it to the row factory and return its result.
15031487
Return ``None`` if no more data is available.
@@ -1591,6 +1575,22 @@ Cursor objects
15911575
including :abbr:`CTE (Common Table Expression)` queries.
15921576
It is only updated by the :meth:`execute` and :meth:`executemany` methods.
15931577

1578+
.. attribute:: row_factory
1579+
1580+
Control how a row fetched from this :class:`!Cursor` is represented.
1581+
If ``None``, a row is represented as a :class:`tuple`.
1582+
Can be set to the included :class:`sqlite3.Row`;
1583+
or a :term:`callable` that accepts two arguments,
1584+
a :class:`Cursor` object and the :class:`!tuple` of row values,
1585+
and returns a custom object representing an SQLite row.
1586+
1587+
Defaults to what :attr:`Connection.row_factory` was set to
1588+
when the :class:`!Cursor` was created.
1589+
Assigning to this attribute does not affect
1590+
:attr:`Connection.row_factory` of the parent connection.
1591+
1592+
See :ref:`sqlite3-howto-row-factory` for more details.
1593+
15941594

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

1612-
Two row objects compare equal if have equal columns and equal members.
1612+
Two :class:`!Row` objects compare equal
1613+
if they have identical column names and values.
1614+
1615+
See :ref:`sqlite3-howto-row-factory` for more details.
16131616

16141617
.. method:: keys
16151618

@@ -1620,21 +1623,6 @@ Row objects
16201623
.. versionchanged:: 3.5
16211624
Added support of slicing.
16221625

1623-
Example:
1624-
1625-
.. doctest::
1626-
1627-
>>> con = sqlite3.connect(":memory:")
1628-
>>> con.row_factory = sqlite3.Row
1629-
>>> res = con.execute("SELECT 'Earth' AS name, 6378 AS radius")
1630-
>>> row = res.fetchone()
1631-
>>> row.keys()
1632-
['name', 'radius']
1633-
>>> row[0], row["name"] # Access by index and name.
1634-
('Earth', 'Earth')
1635-
>>> row["RADIUS"] # Column names are case-insensitive.
1636-
6378
1637-
16381626

16391627
.. _sqlite3-blob-objects:
16401628

@@ -2358,6 +2346,96 @@ can be found in the `SQLite URI documentation`_.
23582346
.. _SQLite URI documentation: https://www.sqlite.org/uri.html
23592347

23602348

2349+
.. _sqlite3-howto-row-factory:
2350+
2351+
How to create and use row factories
2352+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2353+
2354+
By default, :mod:`!sqlite3` represents each row as a :class:`tuple`.
2355+
If a :class:`!tuple` does not suit your needs,
2356+
you can use the :class:`sqlite3.Row` class
2357+
or a custom :attr:`~Cursor.row_factory`.
2358+
2359+
While :attr:`!row_factory` exists as an attribute both on the
2360+
:class:`Cursor` and the :class:`Connection`,
2361+
it is recommended to set :class:`Connection.row_factory`,
2362+
so all cursors created from the connection will use the same row factory.
2363+
2364+
:class:`!Row` provides indexed and case-insensitive named access to columns,
2365+
with minimal memory overhead and performance impact over a :class:`!tuple`.
2366+
To use :class:`!Row` as a row factory,
2367+
assign it to the :attr:`!row_factory` attribute:
2368+
2369+
.. doctest::
2370+
2371+
>>> con = sqlite3.connect(":memory:")
2372+
>>> con.row_factory = sqlite3.Row
2373+
2374+
Queries now return :class:`!Row` objects:
2375+
2376+
.. doctest::
2377+
2378+
>>> res = con.execute("SELECT 'Earth' AS name, 6378 AS radius")
2379+
>>> row = res.fetchone()
2380+
>>> row.keys()
2381+
['name', 'radius']
2382+
>>> row[0] # Access by index.
2383+
'Earth'
2384+
>>> row["name"] # Access by name.
2385+
'Earth'
2386+
>>> row["RADIUS"] # Column names are case-insensitive.
2387+
6378
2388+
2389+
You can create a custom :attr:`~Cursor.row_factory`
2390+
that returns each row as a :class:`dict`, with column names mapped to values:
2391+
2392+
.. testcode::
2393+
2394+
def dict_factory(cursor, row):
2395+
fields = [column[0] for column in cursor.description]
2396+
return {key: value for key, value in zip(fields, row)}
2397+
2398+
Using it, queries now return a :class:`!dict` instead of a :class:`!tuple`:
2399+
2400+
.. doctest::
2401+
2402+
>>> con = sqlite3.connect(":memory:")
2403+
>>> con.row_factory = dict_factory
2404+
>>> for row in con.execute("SELECT 1 AS a, 2 AS b"):
2405+
... print(row)
2406+
{'a': 1, 'b': 2}
2407+
2408+
The following row factory returns a :term:`named tuple`:
2409+
2410+
.. testcode::
2411+
2412+
from collections import namedtuple
2413+
2414+
def namedtuple_factory(cursor, row):
2415+
fields = [column[0] for column in cursor.description]
2416+
cls = namedtuple("Row", fields)
2417+
return cls._make(row)
2418+
2419+
:func:`!namedtuple_factory` can be used as follows:
2420+
2421+
.. doctest::
2422+
2423+
>>> con = sqlite3.connect(":memory:")
2424+
>>> con.row_factory = namedtuple_factory
2425+
>>> cur = con.execute("SELECT 1 AS a, 2 AS b")
2426+
>>> row = cur.fetchone()
2427+
>>> row
2428+
Row(a=1, b=2)
2429+
>>> row[0] # Indexed access.
2430+
1
2431+
>>> row.b # Attribute access.
2432+
2
2433+
2434+
With some adjustments, the above recipe can be adapted to use a
2435+
:class:`~dataclasses.dataclass`, or any other custom class,
2436+
instead of a :class:`~collections.namedtuple`.
2437+
2438+
23612439
.. _sqlite3-explanation:
23622440

23632441
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