Row factories#

Cursor’s fetch* methods, by default, return the records received from the database as tuples. This can be changed to better suit the needs of the programmer by using custom row factories.

The module psycopg.rows exposes several row factories ready to be used. For instance, if you want to return your records as dictionaries, you can use dict_row:

>>> from psycopg.rows import dict_row

>>> conn = psycopg.connect(DSN, row_factory=dict_row)

>>> conn.execute("select 'John Doe' as name, 33 as age").fetchone()
{'name': 'John Doe', 'age': 33}

The row_factory parameter is supported by the connect() method and the cursor() method. Later usage of row_factory overrides a previous one. It is also possible to change the Connection.row_factory or Cursor.row_factory attributes to change what they return:

>>> cur = conn.cursor(row_factory=dict_row)
>>> cur.execute("select 'John Doe' as name, 33 as age").fetchone()
{'name': 'John Doe', 'age': 33}

>>> from psycopg.rows import namedtuple_row
>>> cur.row_factory = namedtuple_row
>>> cur.execute("select 'John Doe' as name, 33 as age").fetchone()
Row(name='John Doe', age=33)

If you want to return objects of your choice you can use a row factory generator, for instance class_row or args_row, or you can write your own row factory:

>>> from dataclasses import dataclass

>>> @dataclass
... class Person:
...     name: str
...     age: int
...     weight: Optional[int] = None

>>> from psycopg.rows import class_row
>>> cur = conn.cursor(row_factory=class_row(Person))
>>> cur.execute("select 'John Doe' as name, 33 as age").fetchone()
Person(name='John Doe', age=33, weight=None)

Note

The choice of a row_factory in a Connection or a Cursor constructor affects how the object is annotated for static type checking.

For instance, declaring a row_factory=dict_row will result in the cursors’ executeany() annotated as returning list[dict[str, Any]] instead of list[tuple[Any, ...]].

Please check Static Typing for more details.

Creating new row factories#

A row factory is a callable that accepts a Cursor object and returns another callable, a row maker, which takes raw data (as a sequence of values) and returns the desired object.

The role of the row factory is to inspect a query result (it is called after a query is executed and properties such as description and pgresult are available on the cursor) and to prepare a callable which is efficient to call repeatedly (because, for instance, the names of the columns are extracted, sanitised, and stored in local variables).

Formally, these objects are represented by the RowFactory and RowMaker protocols.

RowFactory objects can be implemented as a class, for instance:

from typing import Any, Sequence
from psycopg import Cursor

class DictRowFactory:
    def __init__(self, cursor: Cursor[Any]):
        self.fields = [c.name for c in cursor.description]

    def __call__(self, values: Sequence[Any]) -> dict[str, Any]:
        return dict(zip(self.fields, values))

or as nested functions:

def dict_row_factory(cursor: Cursor[Any]) -> RowMaker[dict[str, Any]]:
    fields = [c.name for c in cursor.description]

    def make_row(values: Sequence[Any]) -> dict[str, Any]:
        return dict(zip(fields, values))

    return make_row

These can then be used by specifying a row_factory argument in Connection.connect(), Connection.cursor(), or by setting the Connection.row_factory attribute.

conn = psycopg.connect(row_factory=DictRowFactory)
cur = conn.execute("SELECT first_name, last_name, age FROM persons")
person = cur.fetchone()
print(f"{person['first_name']} {person['last_name']}")