psycopg (v3)
psycopg v3 — the successor to psycopg2. Completely rewritten. Install is 'psycopg' (no number), import is 'import psycopg'. Supports both sync and native async (AsyncConnection). Current version: 3.3.3 (Mar 2026). NOT backward compatible with psycopg2 — several behavior changes. For new projects use psycopg over psycopg2. Django 4.2+ supports psycopg v3.
Warnings
- breaking 'with connection' behavior changed from psycopg2. In psycopg2 it manages the transaction. In psycopg v3 it closes the connection. Major silent bug when migrating.
- breaking cursor_factory moved from cursor() to connect(). psycopg2 pattern: conn.cursor(cursor_factory=DictCursor). psycopg v3: psycopg.connect(..., row_factory=dict_row).
- breaking Package name changed. Install: 'psycopg' not 'psycopg3' or 'psycopg2'. Import: 'import psycopg' not 'import psycopg2'. LLMs confuse the package names.
- gotcha psycopg2 and psycopg cannot be used interchangeably in the same codebase — APIs differ. Cannot drop-in replace 'import psycopg2' with 'import psycopg'.
- gotcha row_factory=dict_row is imported from psycopg.rows, not from psycopg directly. 'from psycopg import dict_row' fails.
- gotcha COPY command API changed. psycopg2 had copy_expert(), copy_from(), copy_to(). psycopg v3 has a single copy() method with unified interface.
Install
-
pip install psycopg -
pip install psycopg[binary] -
pip install psycopg[pool]
Imports
- connect (sync)
import psycopg # row_factory for dict rows — moved to connect() not cursor() with psycopg.connect( 'postgresql://user:pass@localhost/mydb', row_factory=psycopg.rows.dict_row ) as conn: with conn.cursor() as cur: cur.execute('SELECT * FROM users WHERE id = %s', (user_id,)) row = cur.fetchone() print(row['name']) - AsyncConnection
import psycopg import asyncio async def main(): async with await psycopg.AsyncConnection.connect( 'postgresql://user:pass@localhost/mydb', row_factory=psycopg.rows.dict_row ) as conn: async with conn.cursor() as cur: await cur.execute('SELECT * FROM users') rows = await cur.fetchall() for row in rows: print(row['name']) asyncio.run(main())
Quickstart
# pip install psycopg
import psycopg
from psycopg.rows import dict_row
# Sync usage
with psycopg.connect(
'postgresql://user:pass@localhost/mydb',
row_factory=dict_row
) as conn:
with conn.cursor() as cur:
cur.execute(
'SELECT id, name FROM users WHERE active = %s',
(True,)
)
for row in cur.fetchall():
print(row['name']) # dict access
conn.commit()