Curso de Python / Guardar y consultar datos con sqlite3 y transacciones

sqlite3 permite guardar datos SQL en un archivo sin instalar un servidor de base de datos separado. Pasa los valores como parámetros y define explícitamente cuándo se confirman o revierten las modificaciones y quién cierra la conexión.

Preparar el entorno y la política de transacciones

Este ejemplo requiere Python 3.12 o posterior por el argumento autocommit=False. Utiliza una carpeta de prácticas nueva: ledger-demo.db será una base desechable, no un archivo de producción. SQLite es un motor integrado; sus condiciones de concurrencia y operación son distintas de las de un servidor de base de datos.

El bloque with con confirma la transacción si termina correctamente o la revierte si hay un error. No cierra la conexión; el finally realiza esa tarea por separado. Abrir una conexión o salir de una función no equivale por sí solo a haber guardado correctamente todos los cambios.

Insertar un gasto y consultar el resultado

Guarda el programa completo como sqlite_demo.py y ejecútalo con python sqlite_demo.py. El módulo forma parte de las distribuciones habituales de Python; si tu distribución no lo incluye, revisa su documentación.

import sqlite3
MAX_AMOUNT = 1_000_000_000_000
amount = 8000
if type(amount) is not int or not 0 <= amount <= MAX_AMOUNT:
    raise ValueError("amount outside practice range")
con = sqlite3.connect("ledger-demo.db", autocommit=False)
try:
    with con:
        con.execute("CREATE TABLE IF NOT EXISTS expenses (id TEXT PRIMARY KEY, name TEXT NOT NULL, amount INTEGER NOT NULL CHECK(amount BETWEEN 0 AND 1000000000000))")
        con.execute("INSERT INTO expenses VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET name=excluded.name, amount=excluded.amount", ("e1", "lunch", amount))
    print(con.execute("SELECT name, amount FROM expenses ORDER BY id").fetchall())
finally:
    con.close()
[('lunch', 8000)]

El identificador e1 es la clave primaria. La cláusula ON CONFLICT actualiza esa misma fila al repetir el programa, en lugar de crear una copia del gasto. El ORDER BY fija el orden de consulta; no dependas del orden observado en una consulta sin esa cláusula.

Separar parámetros, estructura y reglas de almacenamiento

Los signos de interrogación representan valores, proporcionados en la tupla del segundo argumento de execute. Una tupla de un elemento necesita la coma, como (value,). Los nombres de tablas y las expresiones de ordenación forman parte de la estructura SQL: si deben variar, selecciónalos desde una lista controlada en vez de interpolar entrada libre.

El programa acepta enteros entre cero y un billón, es decir, 1.000.000.000.000. Los enteros de Python pueden superar el rango de almacenamiento de SQLite, cuyo INTEGER utiliza enteros con signo de 64 bits. La validación previa evita llegar al enlace de parámetros con valores fuera del contrato; CHECK añade una regla en la base para otras rutas de escritura.

CREATE TABLE IF NOT EXISTS no modifica una restricción antigua. Una base existente necesita una migración diseñada y respaldada. Consulta la gestión de transacciones de sqlite3 y los tipos de almacenamiento de SQLite para conocer estas condiciones.

Comprobar repeticiones y rechazos

  1. Ejecuta el programa dos veces y confirma que solo existe una fila.
  2. Prueba un importe negativo y observa el ValueError anterior al SQL. Comprueba que el importe guardado sigue intacto.
  3. Restaura el valor y añade otro identificador para observar el orden explícito de consulta.

Una escritura SQL directa sigue sujeta al CHECK. Ante bloqueos, revisa transacciones largas y conexiones sin cerrar; no añadas un DROP TABLE para facilitar repeticiones sobre un archivo desconocido. La gestión de excepciones y el proyecto final de gastos desarrollan estas responsabilidades.