Skip to content

prefer-class-row

Avoid fetching a Psycopg dictionary row only to construct the same model manually.

Why

Folding an immediate model conversion into the row factory removes an intermediate mapping and gives fetches the declared model type.

Fix

Use row_factory=class_row(Model) when selected column names map directly to that model; retain dict_row for dynamic, derived, connection-wide, or multi-shape results.

Examples

Before — flagged Fetched dictionary is immediately converted
app/task_store.py
from psycopg.rows import dict_row
async def load(conn):
async with conn.cursor(row_factory=dict_row) as cursor:
await cursor.execute("SELECT id, state FROM task")
row = await cursor.fetchone()
return Task.model_validate(row)
After — preferred Cursor constructs the model directly
app/task_store.py
from psycopg.rows import class_row
async def load(conn):
async with conn.cursor(row_factory=class_row(Task)) as cursor:
return await cursor.fetchone()