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)
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 a plain function:
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']}")