SQLite v Pythonu: modul sqlite3 prakticky a bezpečně
Jak pracovat s SQLite v Pythonu – připojení, parametrizované dotazy, transakce, Row factory, indexy, WAL režim a zálohy. Bez SQL injection a zamčených databází.
SQLite je databáze v jednom souboru, bez serveru a bez konfigurace. Modul sqlite3 je součástí standardní knihovny Pythonu, takže nic neinstalujete. Pro skripty, menší weby, boty a desktopové aplikace je to často nejlepší volba.
Připojení a vytvoření tabulky
import sqlite3
conn = sqlite3.connect("app.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS ukoly (
id INTEGER PRIMARY KEY,
nazev TEXT NOT NULL,
hotovo INTEGER NOT NULL DEFAULT 0,
vytvoreno TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.commit()
Soubor app.db se vytvoří automaticky. Pro testy použijte ":memory:" – databázi jen v paměti.
INTEGER PRIMARY KEY je v SQLite alias pro interní rowid, takže se hodnota přiděluje automaticky.
Parametrizované dotazy – jediný správný způsob
nazev = input("Název úkolu: ")
# ŠPATNĚ – SQL injection:
conn.execute(f"INSERT INTO ukoly (nazev) VALUES ('{nazev}')")
# SPRÁVNĚ:
conn.execute("INSERT INTO ukoly (nazev) VALUES (?)", (nazev,))
Pozor na čárku v (nazev,) – bez ní to není n-tice. Lze použít i pojmenované parametry:
conn.execute(
"UPDATE ukoly SET hotovo = :hotovo WHERE id = :id",
{"hotovo": 1, "id": 5},
)
Parametry fungují jen pro hodnoty. Názvy tabulek a sloupců parametrizovat nelze – pokud je musíte skládat dynamicky, ověřte je proti pevnému seznamu povolených hodnot.
Čtení dat
conn.row_factory = sqlite3.Row
for radek in conn.execute("SELECT id, nazev, hotovo FROM ukoly WHERE hotovo = ?", (0,)):
print(radek["id"], radek["nazev"])
jeden = conn.execute("SELECT * FROM ukoly WHERE id = ?", (1,)).fetchone()
if jeden is None:
print("Nenalezeno")
sqlite3.Row umožňuje přístup podle názvu sloupce i indexu a převod na slovník přes dict(radek).
Transakce a context manager
Modul sqlite3 ve výchozím režimu automaticky otevírá transakci před INSERT/UPDATE/DELETE, ale commit musíte udělat sami. Nejpohodlnější je spojení použít jako context manager:
with conn:
conn.execute("INSERT INTO ukoly (nazev) VALUES (?)", ("A",))
conn.execute("INSERT INTO ukoly (nazev) VALUES (?)", ("B",))
# commit proběhne automaticky; při výjimce rollback
Pozor: with conn: spojení nezavírá. Pro uzavření použijte conn.close() nebo contextlib.closing.
Hromadné vkládání
data = [("Úkol 1",), ("Úkol 2",), ("Úkol 3",)]
with conn:
conn.executemany("INSERT INTO ukoly (nazev) VALUES (?)", data)
Tisíce řádků v jedné transakci jsou řádově rychlejší než tisíce samostatných commitů.
Cizí klíče jsou ve výchozím stavu vypnuté
Tohle překvapí každého:
conn.execute("PRAGMA foreign_keys = ON")
Musí se nastavit pro každé spojení. Bez toho REFERENCES a ON DELETE CASCADE nic nedělají.
WAL režim a souběžný přístup
SQLite zvládá mnoho čtenářů, ale jen jednoho zapisovatele naráz. Pro web nebo bota s více vlákny zapněte WAL:
conn.execute("PRAGMA journal_mode = WAL") # trvalé nastavení souboru
conn = sqlite3.connect("app.db", timeout=10)
- WAL dovolí čtení během zápisu.
- timeout (výchozí 5 s) určuje, jak dlouho čekat na zámek, než přijde
database is locked. - Transakce držte krátké – nedělejte síťové požadavky uprostřed otevřené transakce.
Ve WAL režimu vedle databáze uvidíte soubory app.db-wal a app.db-shm. Jsou součástí databáze, nemažte je za běhu.
Indexy
conn.execute("CREATE INDEX IF NOT EXISTS idx_ukoly_hotovo ON ukoly(hotovo)")
Zda dotaz index využije, ověříte:
for r in conn.execute("EXPLAIN QUERY PLAN SELECT * FROM ukoly WHERE hotovo = 0"):
print(r)
SCAN ukoly znamená průchod celou tabulkou, SEARCH ... USING INDEX je to, co chcete.
Datum a čas
SQLite nemá datový typ pro datum. Ukládejte ISO 8601 text (2026-10-12T14:30:00) – dá se řadit i porovnávat jako řetězec. Výchozí převodníky datetime v modulu sqlite3 jsou od Pythonu 3.12 označené jako zastaralé, takže převod dělejte explicitně přes datetime.isoformat() a datetime.fromisoformat().
Zálohování za běhu
Kopírovat soubor cp během zápisu není bezpečné. Použijte backup API:
src = sqlite3.connect("app.db")
dst = sqlite3.connect("zaloha.db")
with dst:
src.backup(dst)
dst.close(); src.close()
Nebo z příkazové řádky: sqlite3 app.db ".backup zaloha.db".
Kdy SQLite nestačí
- Mnoho souběžných zapisovatelů (desítky požadavků za sekundu se zápisem).
- Více aplikačních serverů sdílejících jednu databázi přes síť – SQLite na síťovém disku (NFS) není spolehlivé.
- Potřeba uživatelů a oprávnění na úrovni databáze.
V takovém případě přejděte na PostgreSQL. Pokud píšete SQL ručně a parametrizovaně, migrace bude jednodušší, než se zdá.