Módulo 8: Conexión a Bases de Datos con SQLite

Tema 26: ¿Qué es una base de datos? Introducción a SQL

Objetivo del tema

Comprender el concepto de base de datos, su utilidad en el desarrollo de aplicaciones, y familiarizarse con los fundamentos del lenguaje SQL (Structured Query Language), base para interactuar con sistemas de gestión de bases de datos como SQLite, MySQL, o PostgreSQL.

1. ¿Qué es una base de datos?

Una base de datos (BD) es un conjunto de información estructurada y organizada que permite almacenar, consultar, modificar y eliminar datos de manera eficiente.

En términos simples, es como una hoja de cálculo inteligente que guarda datos en tablas, las cuales se pueden relacionar entre sí.

Ejemplo cotidiano

Imagina una aplicación de gestión de biblioteca:

ID Título Autor Año Disponible
1 El Quijote Miguel de Cervantes 1605
2 Cien años de soledad Gabriel García Márquez 1967 No

Esta tabla podría formar parte de una base de datos llamada biblioteca.db.

2. Tipos de bases de datos

1. Bases de datos relacionales (SQL)

Organizan la información en tablas (similares a hojas de Excel), y permiten relacionarlas entre sí mediante claves.

Ejemplos:

  • SQLite (ligera, ideal para proyectos pequeños o locales)
  • MySQL, PostgreSQL, MariaDB (más potentes, ideales para servidores)

2. Bases de datos no relacionales (NoSQL)

Almacenan la información de forma más flexible (documentos, grafos, pares clave-valor). Ejemplo: MongoDB, Firebase, Redis.

En este módulo trabajaremos con SQLite, una base de datos relacional integrada en Python, sin necesidad de instalar servidores adicionales.

3. Conceptos básicos de una base de datos relacional

Concepto Descripción Ejemplo
Tabla Estructura que almacena datos en filas y columnas. usuarios, productos
Fila (registro) Cada elemento o entrada de una tabla. Un usuario específico
Columna (campo) Atributo o propiedad de los datos. nombre, edad, email
Clave primaria (PRIMARY KEY) Identificador único de cada fila. id
Clave foránea (FOREIGN KEY) Relación entre dos tablas. id_usuario en la tabla pedidos

4. ¿Qué es SQL?

SQL (Structured Query Language) es el lenguaje estándar para crear, consultar y modificar bases de datos relacionales.

Principales tipos de sentencias SQL:

Tipo Descripción Ejemplo
DDL (Data Definition Language) Define la estructura de la base de datos. CREATE TABLE, DROP TABLE
DML (Data Manipulation Language) Manipula los datos dentro de las tablas. INSERT, UPDATE, DELETE
DQL (Data Query Language) Consulta la información. SELECT
DCL (Data Control Language) Controla los permisos y usuarios. GRANT, REVOKE

5. Sentencias SQL más comunes

Crear una tabla

CREATE TABLE usuarios (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    nombre TEXT NOT NULL,
    edad INTEGER,
    correo TEXT
);

Insertar datos

INSERT INTO usuarios (id, nombre, edad, correo)
VALUES (26, 'Ana', 25, '[ana@mail.com](mailto:ana@mail.com)');

Consultar datos

SELECT * FROM usuarios;

Actualizar datos

UPDATE usuarios
SET edad = 26
WHERE nombre = 'Ana';

Eliminar datos

DELETE FROM usuarios
WHERE nombre = 'Ana';

6. Relaciones entre tablas

Las bases de datos relacionales permiten vincular información de distintas tablas. Por ejemplo, una tabla usuarios puede estar relacionada con pedidos mediante una clave foránea (id_usuario):

usuarios

id nombre correo
1 Ana ana@x.com
2 Luis luis@x.com

pedidos

id id_usuario producto
1 1 Teclado
2 1 Ratón
3 2 Monitor

Aquí, id_usuario en pedidos es una clave foránea que conecta con id de usuarios.

7. ¿Por qué usar SQLite en Python?

  • Ligera y rápida: no requiere instalación ni servidor.
  • Integrada en Python (módulo estándar sqlite3).
  • Ideal para pruebas y proyectos pequeños o medianos.
  • Usa archivos .db o .sqlite que se pueden transportar fácilmente.

En los próximos temas aprenderás a conectar, crear tablas, insertar datos y consultarlos desde Python usando sqlite3.

8. Actividades prácticas

Ejercicio 1

Define qué tipo de base de datos usarías para cada caso:

  1. Aplicación móvil para registrar gastos personales.
  2. Sistema de reservas de vuelos con millones de registros.
  3. Plataforma de redes sociales que maneja fotos y comentarios.

Ejercicio 2

Crea, en un documento de texto o en SQLite, las siguientes tablas:

  • usuarios(id, nombre, edad, correo)
  • pedidos(id, id_usuario, producto)

Relaciona ambas tablas mediante una clave foránea.

Ejercicio 3

Escribe las sentencias SQL necesarias para:

  1. Crear la tabla clientes con los campos id, nombre, telefono.
  2. Insertar tres clientes.
  3. Mostrar todos los registros con SELECT *.

Evaluación del tema

Criterios de evaluación:

  • Comprende qué es una base de datos y su propósito.
  • Distingue entre bases de datos relacionales y no relacionales.
  • Identifica los componentes principales de una base de datos (tablas, registros, claves).
  • Conoce las operaciones básicas de SQL (crear, insertar, consultar, actualizar, eliminar).

Tema 27: SQLite y su uso con sqlite3 (módulo estándar de Python)

Objetivo del tema

Aprender a conectar una aplicación Python a una base de datos SQLite, crear tablas, insertar, leer, modificar y eliminar datos utilizando el módulo estándar sqlite3, sin necesidad de instalar librerías externas.

1. ¿Qué es SQLite?

SQLite es una base de datos relacional ligera, embebida en Python. A diferencia de otros motores (como MySQL o PostgreSQL), no necesita servidor, ya que toda la base de datos se guarda en un único archivo .db o .sqlite.

Es ideal para:

  • Aplicaciones pequeñas o medianas.
  • Fases de desarrollo o pruebas.
  • Aplicaciones de escritorio o móviles (Android, por ejemplo).

En Python, trabajaremos con SQLite a través del módulo estándar sqlite3.

2. Conectarse a una base de datos

Para comenzar a trabajar, primero debemos importar el módulo sqlite3 y crear una conexión con un archivo .db.

Si el archivo no existe, SQLite lo crea automáticamente.

3. El cursor: primeras operaciones

Una vez conectados, se utiliza un cursor para ejecutar instrucciones SQL, y commit() guarda los cambios en el archivo .db. Un primer ciclo completo — crear una tabla, insertar un registro y consultarlo:

Fíjate en los ? del INSERT: son marcadores de posición que evitan concatenar valores del usuario dentro de la consulta (el origen de las inyecciones SQL). El Tema 29 está dedicado íntegramente a ello.

Las operaciones CRUD completas (INSERT, SELECT, UPDATE y DELETE) se desarrollan paso a paso en el Tema 28.

4. Uso del contexto with

El uso de with simplifica la gestión de recursos y cierra automáticamente la conexión, incluso si ocurre un error.

No necesitas llamar a commit() ni close() manualmente.

5. Manejo de errores

Podemos controlar errores con try-except para evitar que el programa se detenga ante fallos.

6. Ejemplo completo

7. Actividades

Ejercicio 1

Crea una base de datos llamada agenda.db con una tabla contactos que contenga:

  • id (clave primaria)
  • nombre
  • telefono
  • email

Inserta tres contactos y muestra todos los registros.

Ejercicio 2

Reescribe el ejercicio anterior usando el bloque with para gestionar la conexión.

Ejercicio 3

Provoca un error consultando una tabla que no existe y captúralo con except sqlite3.Error.

Evaluación del tema

Criterios de evaluación:

  • Conecta correctamente una aplicación Python a SQLite.
  • Crea tablas e inserta y consulta datos con INSERT y SELECT.
  • Aplica correctamente el uso de with y try-except.
  • Utiliza marcadores ? en las consultas en lugar de concatenar valores.

Tema 28: Crear tablas, insertar, leer, actualizar y eliminar registros

Objetivo del tema

Aprender a manipular datos en una base de datos SQLite desde Python, utilizando el módulo sqlite3. El alumnado aprenderá a crear tablas, insertar registros, consultar información, actualizar y eliminar datos mediante sentencias SQL ejecutadas desde el código Python.

1. Crear una tabla

Antes de trabajar con los datos, debemos crear una tabla en la base de datos donde almacenarlos.

IF NOT EXISTS evita errores si la tabla ya existe. La base de datos se guarda en un archivo llamado biblioteca.db.

2. Insertar registros

Podemos agregar nuevos datos usando INSERT INTO. Se recomienda usar parámetros (?) para evitar inyección SQL.

commit() guarda los cambios realizados. Los valores True y False se almacenan como 1 y 0 en SQLite.

3. Leer registros (SELECT)

Para consultar los datos de una tabla usamos SELECT.

También se pueden filtrar resultados con WHERE:

4. Actualizar registros (UPDATE)

Podemos modificar la información de uno o varios registros.

Esto marca el libro 1984 como no disponible (0 = False).

5. Eliminar registros (DELETE)

Podemos eliminar filas con DELETE FROM.

Precaución: Si olvidas el WHERE, ¡se eliminarán todos los registros de la tabla!

6. Consultas más complejas

Podemos combinar condiciones:

7. Uso de funciones y estructuras

Podemos crear funciones para reutilizar operaciones:

8. Buenas prácticas

Usa with para asegurar que la conexión se cierre automáticamente. Utiliza consultas parametrizadas (?) para evitar inyección SQL. Llama a commit() después de cualquier cambio (inserción, actualización o borrado). Usa CREATE TABLE IF NOT EXISTS para no sobrescribir bases de datos existentes.

9. Actividades prácticas

Ejercicio 1

Crea una base de datos empresa.db con una tabla empleados que contenga:

  • id (autoincremental)
  • nombre
  • departamento
  • salario

Inserta tres empleados.

Ejercicio 2

Muestra todos los empleados cuyo salario sea mayor a 2000 €.

Ejercicio 3

Actualiza el salario de un empleado específico y vuelve a mostrar la tabla completa.

Ejercicio 4

Elimina un empleado y confirma que se ha eliminado correctamente.

Reto adicional

Modifica los ejercicios para que todas las operaciones estén encapsuladas en funciones (crear_tabla(), insertar_empleado(), etc.) y se ejecuten en un programa principal (main) que pregunte al usuario qué desea hacer.

Evaluación del tema

Criterios de evaluación:

  • Comprende el flujo CRUD: Create, Read, Update, Delete.
  • Aplica correctamente sqlite3 para manipular datos.
  • Utiliza consultas parametrizadas seguras.
  • Estructura el código en funciones reutilizables.
  • Finaliza los ejercicios con resultados funcionales.

Tema 29: Consultas parametrizadas y protección contra inyecciones SQL

Objetivo del tema

Comprender los riesgos de las inyecciones SQL y aprender a evitarlos mediante el uso de consultas parametrizadas en Python con sqlite3. El alumnado aplicará prácticas seguras para manipular datos en una base de datos desde una aplicación.

1. ¿Qué es una inyección SQL?

Una inyección SQL es una técnica utilizada por atacantes para manipular las consultas SQL que ejecuta una aplicación, con el fin de:

  • Acceder a información no autorizada.
  • Modificar o eliminar datos.
  • Obtener control sobre el sistema.

Ejemplo (vulnerable):

Si el usuario introduce:

usuario: admin
contraseña: ' OR '1'='1

La consulta resultante sería:

SELECT * FROM usuarios WHERE nombre='admin' AND contrasena='' OR '1'='1'

Esto devuelve todos los usuarios, permitiendo acceso sin contraseña.

2. Consultas parametrizadas: la solución segura

En lugar de insertar directamente los valores en la cadena SQL, usamos parámetros (? en SQLite).

Ejemplo seguro:

El motor sqlite3 se encarga internamente de escapar los caracteres especiales, impidiendo la inyección SQL.

3. Ventajas del uso de consultas parametrizadas

Evitan la manipulación maliciosa de la consulta. Mejoran la legibilidad y mantenimiento del código. Permiten reutilizar la misma consulta con distintos parámetros. Aumentan la seguridad sin complicar la sintaxis.

4. Tipos de parámetros en SQLite (Python)

SQLite con sqlite3 permite tres estilos de parámetros:

Estilo Ejemplo Comentario
Posicional (?) "SELECT * FROM usuarios WHERE id = ?" Más común
Nombrado (:nombre) "SELECT * FROM usuarios WHERE nombre = :nombre" Más legible
Mapeado (diccionario) "SELECT * FROM usuarios WHERE id = :id" + {"id": 1} Muy útil con datos dinámicos

Ejemplo con parámetros nombrados:

5. Ejemplo completo: CRUD seguro

Nota: Incluso en proyectos sencillos, nunca concatenes datos del usuario en la consulta SQL.

6. Ejemplo de ataque frustrado

El sistema no ejecuta código SQL malicioso, porque los parámetros están correctamente escapados.

7. Buenas prácticas de seguridad con SQLite

    1. Siempre usa consultas parametrizadas.
    1. No muestres mensajes detallados de error al usuario final.
    1. Usa contraseñas cifradas (por ejemplo, con hashlib).
    1. Limita los permisos de escritura/lectura del archivo .db.
    1. Usa with y try-except para manejar errores.

Ejemplo de manejo de errores:

8. Actividades prácticas

Ejercicio 1

Crea una tabla clientes con los campos:

  • id (clave primaria)
  • nombre
  • correo
  • telefono

Inserta tres clientes usando consultas parametrizadas.

Ejercicio 2

Realiza una consulta para obtener un cliente por nombre. Prueba a introducir el siguiente valor como entrada:

nombre = "' OR '1'='1"

Verifica que tu programa no devuelve todos los registros.

Ejercicio 3

Modifica el programa para:

  • Actualizar el correo de un cliente.
  • Eliminar un cliente por su nombre. Ambas operaciones deben ser parametrizadas.

Reto adicional

Implementa un pequeño sistema de inicio de sesión por consola:

  • El usuario ingresa su nombre y contraseña.
  • Se valida contra la base de datos con consultas seguras.
  • Si falla tres veces, se bloquea el acceso.

Evaluación del tema

Criterios de evaluación:

  • Comprende el riesgo de las inyecciones SQL.
  • Utiliza correctamente consultas parametrizadas (? o :param).
  • Aplica manejo de errores (try-except) y bloque with.
  • Crea ejercicios funcionales y seguros.
  • Adopta buenas prácticas en la gestión de bases de datos.

Tema 30: Integración de la base de datos en proyectos existentes (con POO y funciones)

Objetivo del tema

Aprender a integrar una base de datos en un proyecto Python real aplicando los principios de reutilización de código, encapsulamiento y separación de responsabilidades, utilizando funciones y clases para organizar la lógica de acceso a datos.

1. ¿Por qué integrar la base de datos con POO?

Hasta ahora hemos trabajado con consultas SQL dentro del mismo script. En proyectos reales, esto no escala bien porque:

  • Se repite mucho código (abrir, cerrar, ejecutar consultas).
  • Es difícil mantener y depurar.
  • Las responsabilidades no están separadas (lógica y datos se mezclan).

Solución: crear una clase de gestión de base de datos que centralice todas las operaciones. Esto permite que otros módulos del proyecto (por ejemplo, la interfaz o el backend) solo llamen métodos bien definidos.

2. Ejemplo sin POO (repetitivo)

Aunque funciona, si añadimos más funciones (buscar, eliminar, actualizar…), el código se vuelve largo y repetitivo.

3. Reestructurando con una clase

Podemos crear una clase BaseDatosUsuarios que encapsule toda la lógica de conexión y manejo de usuarios.

4. Uso de la clase desde otro módulo

Podemos importar la clase en otro archivo y utilizarla fácilmente:

Así logramos un código limpio, modular y reutilizable.

5. Integración con un modelo de datos (POO completa)

Podemos representar los usuarios como objetos, y dejar que la clase BaseDatosUsuarios trabaje con ellos.

Y actualizar la clase de base de datos para trabajar con objetos:

Uso:

Ventajas de este enfoque:

  • Código más legible y mantenible.
  • Separa la lógica de datos (acceso a la base) del modelo de negocio (usuarios).
  • Permite escalar hacia proyectos mayores (MVC, API, etc.).

6. Integración con funciones auxiliares

A veces no es necesario usar clases para todo. Podemos tener funciones utilitarias que complementen el trabajo de las clases, por ejemplo, para mostrar datos o inicializar tablas.

7. Integración en un proyecto modular

Una buena estructura de proyecto podría verse así:

proyecto/
    │
    ├── main.py
    ├── modelos/
    │   └── usuario.py
    ├── datos/
    │   └── base_datos.py
    └── utils/
        └── helpers.py
  • usuario.py: contiene la clase Usuario.
  • base_datos.py: contiene BaseDatosUsuarios.
  • helpers.py: funciones auxiliares (mostrar, cargar, etc.).
  • main.py: ejecuta la aplicación principal.

Esto refleja una arquitectura profesional y extensible.

8. Actividades prácticas

Ejercicio 1

Crea una clase Producto con atributos nombre, precio y stock. Crea otra clase BaseDatosProductos con métodos:

  • crear_tabla()
  • agregar(producto)
  • listar_todos()
  • buscar_por_nombre(nombre)

Guarda varios productos y muestra la lista completa.

Ejercicio 2

Modifica tu clase para que use consultas parametrizadas y maneje errores con try-except. Ejemplo: si se intenta insertar un producto con nombre duplicado, muestra un mensaje adecuado.

Ejercicio 3 (reto final del módulo)

Crea un pequeño sistema de gestión de usuarios o productos con:

  • Clases separadas (Usuario, BaseDatosUsuarios).
  • Consultas parametrizadas.
  • Interacción por consola para insertar, listar, eliminar y buscar.

Pista: Combina todo lo aprendido en este módulo (POO, sqlite3, seguridad y modularización).

Evaluación del tema

Criterios de evaluación:

  • Usa clases para encapsular la gestión de la base de datos.
  • Aplica funciones auxiliares donde sea necesario.
  • Integra consultas seguras y manejo de errores.
  • Presenta una estructura modular y clara del proyecto.
  • Código funcional, limpio y comentado.