Source code for django_snapshots.connectors.sqlite

"""SQLiteConnector — uses Python's stdlib sqlite3 .dump() method.

No external binaries required. Works on all platforms.
"""

from __future__ import annotations

import sqlite3
from pathlib import Path
from typing import Any

from django.conf import settings as django_settings

from django_snapshots.exceptions import SnapshotConnectorError


[docs] class SQLiteConnector: """Back up and restore SQLite databases using the stdlib ``sqlite3`` module.""" @staticmethod def _connect(db_name: str) -> sqlite3.Connection: # Django uses SQLite URI filenames (e.g. "file:memorydb_default?mode=memory&cache=shared") # for in-memory shared-cache test databases. Python's sqlite3 only reliably # interprets these as URIs when uri=True is explicitly passed; without it, # Windows treats the string as a literal filename (containing illegal '?'), # causing the connector to open a different database than Django's connection. is_uri = db_name.startswith("file:") return sqlite3.connect(db_name, uri=is_uri)
[docs] def dump(self, db_alias: str, dest: Path) -> dict[str, Any]: """Dump the SQLite database for *db_alias* to a SQL script at *dest*.""" db_path = str(django_settings.DATABASES[db_alias]["NAME"]) dest.parent.mkdir(parents=True, exist_ok=True) try: con = self._connect(db_path) with open(dest, "w", encoding="utf-8") as f: for line in con.iterdump(): f.write(f"{line}\n") con.close() except Exception as exc: raise SnapshotConnectorError( f"SQLite dump failed for alias {db_alias!r}: {exc}" ) from exc return {"format": "sql"}
[docs] def restore(self, db_alias: str, src: Path) -> None: """Restore the SQLite database for *db_alias* from the SQL script at *src*.""" db_path = str(django_settings.DATABASES[db_alias]["NAME"]) try: script = src.read_text(encoding="utf-8") # Close Django's connection before acquiring our own — on Windows, # SQLite's exclusive write lock prevents a second connection from # opening the file while Django's handle is still open. from django.db import connections # noqa: PLC0415 connections[db_alias].close() con = self._connect(db_path) # Drop all existing tables so the dump script can recreate them cleanly. cur = con.cursor() cur.execute("PRAGMA foreign_keys = OFF") tables = cur.execute( "SELECT name FROM sqlite_master WHERE type='table'" " AND name NOT LIKE 'sqlite_%'" ).fetchall() for (table,) in tables: cur.execute(f'DROP TABLE IF EXISTS "{table}"') con.commit() con.executescript(script) con.close() # Close Django's connection so the ORM reconnects and sees the restored data. connections[db_alias].close() except Exception as exc: raise SnapshotConnectorError( f"SQLite restore failed for alias {db_alias!r}: {exc}" ) from exc