Сообщить об ошибке.

Эффективное использование sqlite3

Использование методов быстрого доступа:

Используя нестандартные методы execute(), executemany() и executetescript() объекта Connection, код можно написать более кратко, поскольку не нужно явно создавать (часто лишние) объекты Cursor.

Объекты Cursor создаются неявно и эти методы быстрого вызова возвращают объекты курсора. Таким образом, можно выполнить инструкцию SELECT и выполнить итерацию напрямую, используя только один вызов объекта Connection.

import sqlite3

persons = [
    ("Hugo", "Boss"),
    ("Calvin", "Klein")
    ]

con = sqlite3.connect(":memory:")

# Создать таблицу
con.execute("create table person(firstname, lastname)")

# Заполнить таблицу
con.executemany("insert into person(firstname, lastname) values (?, ?)", persons)

# Распечатать содержимое таблицы
for row in con.execute("select firstname, lastname from person"):
    print(row)

print("I just deleted", con.execute("delete from person").rowcount, "rows")

# close() не является ярлыком и не вызывается автоматически, 
# поэтому объект соединения должен быть закрыт вручную
con.close()

Доступ к столбцам по имени:

Одной из полезных функций модуля sqlite3 является встроенный класс sqlite3.Row, предназначенный для использования в качестве фабрики строк.

Строки, заключенные в этот класс, могут быть доступны как по индексу - как кортежи, так и по имени без учета регистра символов:

import sqlite3

con = sqlite3.connect(":memory:")
con.row_factory = sqlite3.Row

cur = con.cursor()
cur.execute("select 'John' as name, 42 as age")
for row in cur:
    assert row[0] == row["name"]
    assert row["name"] == row["nAmE"]
    assert row[1] == row["age"]
    assert row[1] == row["AgE"]

con.close()

Можно создать собственную фабрику Cursor.row_factory, которая возвращает каждую строку в виде словаря с именами столбцов, сопоставленными со значениями:

def dict_factory(cursor, row):
    fields = [column[0] for column in cursor.description]
    return {key: value for key, value in zip(fields, row)}

Используя его, запросы теперь возвращают словарь вместо кортежа:

>>> con = sqlite3.connect(":memory:")
>>> con.row_factory = dict_factory
>>> for row in con.execute("SELECT 1 AS a, 2 AS b"):
    print(row)
# {'a': 1, 'b': 2}

Следующая фабрика строк возвращает именованный кортеж:

from collections import namedtuple

def namedtuple_factory(cursor, row):
    fields = [column[0] for column in cursor.description]
    cls = namedtuple("Row", fields)
    return cls._make(row)

namedtuple_factory() можно использовать следующим образом:

>>> con = sqlite3.connect(":memory:")
>>> con.row_factory = namedtuple_factory
>>> cur = con.execute("SELECT 1 AS a, 2 AS b")
>>> row = cur.fetchone()
>>> row
# Row(a=1, b=2)
>>> row[0]  # Индексированный доступ.
# 1
>>> row.b   # Доступ к атрибутам.
# 2

С некоторыми изменениями приведенный выше рецепт можно адаптировать для использования класса данных dataclasses.dataclass или любого другого пользовательского класса.

Использование менеджера контекста соединения:

Объекты соединения могут использоваться как контекстные менеджеры, которые автоматически фиксируют или откатывают транзакции. В случае исключения транзакция откатывается, в противном случае сделка совершается:

import sqlite3

con = sqlite3.connect(":memory:")
con.execute("create table person (id integer primary key, firstname varchar unique)")

# впоследствии `con.commit()` вызывается автоматически
with con:
    con.execute("insert into person(firstname) values (?)", ("Joe",))

# `con.rollback()` вызывается после завершения блока `with` с исключением.
# исключение все еще вызывается и должно быть перехвачено
try:
    with con:
        con.execute("insert into person(firstname) values (?)", ("Joe",))
except sqlite3.IntegrityError:
    print("couldn't add Joe twice")

# Объект соединения, используемый в качестве диспетчера контекста, 
# только фиксирует или откатывает транзакции, поэтому объект 
# соединения должен быть закрыт вручную
con.close()