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 <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 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)
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