Python sqlite3 row_factory: строки как словари
По умолчанию sqlite3 возвращает каждую строку как кортеж, и приходится помнить порядок столбцов. connection.row_factory = sqlite3.Row даёт доступ по имени и по индексу, а dict(row) превращает запись в обычный словарь. Это уменьшает связность кода с порядком полей в SELECT.
Читать строки по именам столбцов
import sqlite3
con = sqlite3.connect(":memory:")
con.row_factory = sqlite3.Row
con.execute("CREATE TABLE user (id INTEGER PRIMARY KEY, name TEXT)")
con.execute("INSERT INTO user (name) VALUES (?)", ("Анна",))
row = con.execute("SELECT id, name FROM user").fetchone()
print(row["name"]) # Анна
print(row[0]) # 1
print(dict(row)) # {'id': 1, 'name': 'Анна'}sqlite3.Row поддерживает keys() и итерацию по значениям, поэтому его можно передавать в шаблоны и JSON-сериализаторы после dict(). Доступ по имени работает только для выбранных столбцов; SELECT * сделает код зависимым от схемы, поэтому перечисляйте поля явно.
row_factory задаётся на соединении и действует на все курсоры этого соединения. В пуле соединений настройте фабрику при создании каждого подключения, иначе часть запросов вернёт кортежи. Курсор может переопределить фабрику через cursor.row_factory, но смешивать разные форматы в одном модуле не стоит.
Вернуть dataclass из строки
from dataclasses import dataclass
import sqlite3
@dataclass
class User:
id: int
name: str
def user_factory(cursor, row):
fields = [column[0] for column in cursor.description]
return User(**dict(zip(fields, row)))
con.row_factory = user_factory
user = con.execute("SELECT id, name FROM user").fetchone()
print(user) # User(id=1, name='Анна')Фабрика получает курсор и кортеж значений и может вернуть любой объект. Проверяйте, что имена в cursor.description совпадают с полями dataclass: лишний столбец вызовет TypeError. Для вложенных объектов и связей одного row_factory мало — делайте отдельные запросы или собирайте словарь вручную.
SQLite использует динамическую типизацию, поэтому значение из базы может быть str, int или None независимо от объявления колонки. Приводите типы в фабрике или на уровне репозитория. Для дат используйте detect_types=sqlite3.PARSE_DECLTYPES и адаптеры, но проверяйте их на своей версии Python.
Документация: sqlite3.Row, row_factory.