JSON te permitió guardar el gestor de tareas del curso anterior. Una base de datos aporta otras herramientas para organizar registros y hacer cumplir reglas durante la escritura. SQLite almacena la información en un archivo y no exige administrar un servidor separado. Hoy crearás el módulo almacen.py del proyecto con inserciones que se confirman como una unidad.
Una tabla con reglas explícitas
La tabla articulos tendrá las columnas codigo, nombre y cantidad. El código será la clave primaria: identifica cada registro y no puede repetirse. La cantidad tendrá una restricción entre cero y un millón. Estas reglas acompañan a la validación de Python. Una capa protege las entradas de la aplicación y otra protege el contenido que acepta la tabla.
No estamos aprendiendo todo SQL en una lección. CREATE TABLE define la estructura e INSERT añade registros. Los nombres de tablas y columnas están escritos por nosotros en el programa. Los valores externos viajarán por parámetros. Esa separación evita construir instrucciones mezclando texto SQL y entradas de usuario.
El comienzo de almacen.py
Guarda este bloque junto a modelo.py. La función conectar recibe una ruta y devuelve una conexión abierta. Quien la utilice será responsable de cerrarla. Si la preparación de la tabla falla el propio módulo cerrará la conexión antes de volver a comunicar el error.
def conectar(ruta): conexion = sqlite3.connect(ruta, isolation_level="DEFERRED") try: conexion.execute(""" CREATE TABLE IF NOT EXISTS articulos ( codigo TEXT PRIMARY KEY NOT NULL, nombre TEXT NOT NULL, cantidad INTEGER NOT NULL CHECK(typeof(cantidad) = 'integer' AND cantidad BETWEEN 0 AND 1000000) ) """) conexion.commit() except sqlite3.Error: conexion.close() raise return conexion
def agregar_varios(conexion, articulos): valores = [(a.codigo, a.nombre, a.cantidad) for a in articulos] with conexion: conexion.executemany( "INSERT INTO articulos(codigo, nombre, cantidad) VALUES (?, ?, ?)", valores, ) «`
Entender los signos de interrogación
Cada interrogación representa un valor que SQLite recibe por separado. La tupla contiene esos valores en el mismo orden. No pongas comillas alrededor de las interrogaciones ni sustituyas este mecanismo por una cadena con formato. El motor debe distinguir siempre las instrucciones de los datos, incluso cuando un nombre contenga una comilla.
executemany repite la inserción para una secuencia de tuplas. Hemos preparado primero esa secuencia a partir de los artículos. La función espera objetos previamente validados. No la llamaremos directamente con diccionarios de un CSV. El recorrido será texto, validación, objetos y almacenamiento. Cada paso tendrá una responsabilidad reconocible.
Lo que garantiza la transacción
Con la configuración explícita de este ejemplo las inserciones abren una transacción. El bloque with conexion confirma al salir normalmente y revierte si una excepción atraviesa el bloque. Si un código duplicado provoca un error después de insertar otro artículo del mismo lote, esa inserción anterior también se revierte. Así mantenemos el contrato de todo o nada.
No captures la excepción dentro del bloque para continuar como si nada. Podrías cambiar el comportamiento de confirmación y dejar un resultado parcial. Permitiremos que el error salga del bloque y lo transformaremos en un mensaje en la interfaz. En la última lección comprobarás esta propiedad con una prueba que consulta los datos después del fallo.
Confirmar no significa cerrar
El contexto de la conexión controla la transacción pero no cierra la conexión. Esta diferencia respecto a un archivo es importante. Utilizaremos try y finally en la interfaz para garantizar el cierre. Una conexión abierta mantiene recursos y puede conservar bloqueos. No dependas de que el proceso termine para liberar todo de forma ordenada.
Una transacción tampoco sustituye a una copia de seguridad. Protege la coherencia de un conjunto de operaciones pero no recupera una base borrada por accidente. Separaremos ambas cuestiones. Hoy verificamos que un lote no se guarda a medias. Más adelante prepararemos una copia recuperable del archivo de datos.
Probar sin crear archivos permanentes
SQLite permite usar la ruta especial :memory: para crear una base en memoria. Existirá mientras permanezca abierta esa conexión. Es útil en pruebas porque cada caso puede empezar vacío y no deja un archivo que interfiera con el siguiente. No es una forma de conservar datos entre ejecuciones.
Para una comprobación manual crea una conexión en memoria, construye dos artículos con crear_articulo e insértalos. Consulta el número de registros con SELECT COUNT(*) FROM articulos. Después cierra la conexión. Si abres otra base en memoria estará vacía porque es una base nueva, no una recuperación de la anterior.
Errores habituales
CREATE TABLE IF NOT EXISTS no actualiza una tabla que ya existe con otra estructura. Si cambias el modelo en el futuro necesitarás una migración o una base nueva de prueba. No borres una base con información útil para que desaparezca un error de columnas. Conserva una copia y estudia el cambio que necesitas.
Otro fallo es abrir una ruta equivocada y creer que se han perdido los artículos. SQLite puede crear una base nueva si el archivo no existe. Por eso la interfaz distinguirá inicializar de consultar: solo la inicialización podrá crear el archivo deliberadamente. Las demás operaciones comprobarán que la ruta corresponde a un archivo existente.
Una comprobación adicional
Durante la prueba de reversión consulta también el nombre y la cantidad del registro inicial. No basta con que siga existiendo un solo código: debes comprobar que el conflicto no lo ha sustituido. La instrucción INSERT elegida rechaza la repetición en lugar de actualizar el artículo anterior.
Lo que deberías recordar
- Los valores SQL se pasan mediante parámetros.
- Una clave primaria evita códigos repetidos.
- La transacción confirma o revierte el lote.
- Cerrar y confirmar son operaciones distintas.
Ejercicio práctico
Crea una base en memoria y añade CU001 y LA001 mediante crear_articulo y agregar_varios. Comprueba que el recuento es dos. Cierra la conexión y explica por qué esos datos no reaparecen al crear otra base en memoria.
En una nueva prueba añade primero CU001. Después intenta guardar un lote con CA001 seguido de otro CU001. Debe producirse sqlite3.IntegrityError. Consulta la tabla después del error y confirma que CA001 no quedó guardado. El registro inicial CU001 sí debe permanecer.
Anota las responsabilidades de validación, restricción y transacción. No son tres nombres para lo mismo. La primera acepta entradas, la segunda impide ciertos estados en la tabla y la tercera agrupa escrituras. Conserva este caso porque será una prueba de aceptación central del proyecto final.
Referencia de consulta: Módulo sqlite3 en la documentación oficial de Python.
Siguiente: Consultar datos y presentar resultados claros.








