Cómo usar SQLite en Python

domingo, abril 12, 2009

SQLite (para los que no la conocen) es una pequeña librería que nos permite manejar bases de datos relacionales y transaccionales, sin necesidad de servidor, sin configuración y almacenadas en un solo archivo en la máquina host.

En otras palabras, nuestra base de datos no es más que un archivo almacenado localmente al que nuestra aplicación tendrá acceso mediante llamadas a las rutinas de la librería como en los viejos tiempos. Symbian, Mozilla, Bloomberg y Adobe son solo algunos pequeños usuarios de esta poderosa librería.

Este tutorial está desarrollado usando Debian Squeeze (testing), Python 2.5.4 y SQLite 3. Empezamos por instalar los paquetes necesarios:

# aptitude install python-pysqlite2 sqlitebrowser

Pysqlite2 es un binding de SQLite para python y el sqlitebrowser es una aplicación que nos permitirá administrar la base de datos. Ejecutamos el browser desde un terminal:

$ sqlitebrowser



Vamos al menú File -> New Database para crear una nueva base de datos, le indicamos la ruta y el nombre, para nuestro ejemplo será test.db.



Luego, vamos al menú Edit -> Create Table y creamos una nueva tabla llamada Usuarios con los siguientes campos:

id -> INTEGER PRIMARY KEY
nombre -> TEXT
edad -> NUMERIC
correo -> TEXT
url -> TEXT
visitas -> NUMERIC


Imaginemos que esta tabla es para manejar un sistema donde registraremos a los usuarios que visitan nuestra página web y almacenamos el número de visitas de cada uno (sí, lo se... apesta, pero fue lo mejor que se me ocurrió como ejemplo :S). Debería quedarnos algo así:



Luego que estemos conformes hacemos click en el botón Create y guardamos los cambios en File -> Save Database.

Ahora vamos con la parte divertida, creamos un nuevo archivo de texto con nuestro editor favorito (el mío es SciTE) y lo guardamos como test.py en la misma carpeta donde guardamos la base de datos.

Lo primero que necesitamos para conectarnos a esa base de datos es importar la librería:

import sqlite3

Hacemos la conexión:

connection = sqlite3.connect('test.db')

La mayoría de las operaciones sobre la base de datos se hacen usando un cursor; un objeto que apunta a la base de datos y a través del cual podemos ejecutar instrucciones similares al SQL estándar (pero no idénticas) para obtener, insertar, actualizar o borrar registros.

cursor = connection.cursor()

Muy bonito todo pero se preguntarán ¿Cómo insertamos registros?. La forma recomendada según el librito es usar placeholders (marcadores de posición) puesto que si armamos la cadena usando variables de Python corremos el riesgo de que nos hagan SQL Injection. Es decir, en lugar de hacer esto:

user = 'pedro'
cursor.execute("SELECT * FROM tabla WHERE usuario = '%s'" % user)


Debemos usar tuplas y hacer esto:

user = ('pedro',)
cursor.execute('SELECT * FROM tabla WHERE usuario =?', t)


Insertemos entonces algunos datos para rellenar esa insípida tabla. Primero creamos un arreglo donde esté toda la información a insertar (en este caso es así porque vamos a insertar muchos datos, pero también podemos hacerlo uno por uno)


datos = [
('Pedro Perez', 34, 'pperez@tucorreo.com', '', 4),
('Maria Gomez', 25, 'maria@sucorreo.com', '', 7),
('Pablo Rodriguez', 41, 'pablor@elcorreo.com', 'www.pablo.com', 3),
]


Ahora usamos un iterador para recorrer el arreglo e insertarlo:
for t in datos:
cursor.execute('INSERT INTO Usuarios (nombre,edad,correo,url,visitas) VALUES (?,?,?,?,?)', t)


Pero esperen, aún no hemos completado la tarea. SQLite es un gestor transaccional por lo que debemos realizar "la transacción" usando el comando commit, de lo contrario ningún cambio se hará efectivo sobre el archivo. Esto aplica para todas las operaciones que modifican la base de datos (léase INSERT, UPDATE, DELETE, etc).

connection.commit()

¡Qué bien! Ya sabemos insertar registros, ya nuestra base de datos no es tan insípida ;)

Ahora veamos como obtener la información que acabamos de guardar. Esto es tan sencillo como estructurar la búsqueda y ejecutarla.

Primero veamos como listar todos los registros:

cursor.execute('SELECT * FROM Usuarios')
for row in cursor:
print row

Esto nos imprime en el terminal algo como:

(u'pperez@tucorreo.com', 34, 1, u'Pedro Perez', u'', 4)
(u'maria@sucorreo.com', 25, 2, u'Maria Gomez', u'', 7)
(u'pablor@elcorreo.com', 41, 3, u'Pablo Rodriguez', u'www.pablo.com', 3)


Y si queremos una búsqueda más específica podemos hacer algo como lo siguiente para buscar el nombre y las visitas de todos los registros con id igual a 1:

id = (1, )
cursor.execute("SELECT nombre, visitas FROM Usuarios WHERE id=?", id)
for row in cursor:
print "%s ha realizado %i visitas" % (row[0], row[1])

Para obtener:

Pedro Perez ha realizado 4 visitas

Vamos ahora a actualizar un registro. Es tan fácil como realizar una búsqueda pero usando el comando UPDATE; le decimos qué tabla vamos a actualizar, los campos con sus nuevos valores y la condición que identifica al registro. Para actualizar la edad de Pablo a 24 sería algo como:

values = (24, 'pablor@elcorreo.com', )
cursor.execute("UPDATE Usuarios SET edad=? WHERE correo=?", values)
connection.commit()


Donde values es la tupla que se reemplazará donde están los placeholders. El motor de SQLite interpretará esa búsqueda como si le dijeramos:

cursor.execute("UPDATE Usuarios SET edad=24 WHERE correo='pablor@elcorreo.com'")

La ventaja de los placeholders es que nos permiten crear instrucciones dinámicas y cambiar el contenido de la misma sin tener que estructurarla nuevamente (muy útil para usarla en bucles).

Pero ¿cómo sabemos que realmente se actualizó el registro? Pues sencillo, listemos de nuevo todos los registros:

cursor.execute('SELECT * FROM Usuarios')
for row in cursor:
print row

Y en el terminal veremos algo como (nótese el nuevo valor de la edad de Pablo):

(u'pperez@tucorreo.com', 34, 1, u'Pedro Perez', u'', 4)
(u'maria@sucorreo.com', 25, 2, u'Maria Gomez', u'', 7)
(u'pablor@elcorreo.com', 24, 3, u'Pablo Rodriguez', u'www.pablo.com', 3)


Todo se ve muy bien, pero surge una última pregunta: ¿Cómo rayos puedo borrar un registro?. Simple, la instrucción DELETE obvio ¿no? seguida de la tabla y de la condición que identifica al registro. Borremos a todos los ancianos usuarios mayores de 34 de nuestro registro:

value = (34,)
cursor.execute("DELETE FROM Usuarios WHERE edad >= ?", value)
connection.commit()


Y una vez más mostramos a todo el mundo:
cursor.execute('SELECT * FROM Usuarios')
for row in cursor:
print row


Obteniendo:
(u'maria@sucorreo.com', 25, 2, u'Maria Gomez', u'', 7)
(u'pablor@elcorreo.com', 24, 3, u'Pablo Rodriguez', u'www.pablo.com', 3)


Lo sentimos mucho por Pedro Pérez pero esa es la Ley del Oeste ;)

Para finalizar el testamento la entrada les dejo el archivo test.py después de unir todo lo que hemos hecho hasta ahora:
# -*- coding: utf-8 -*-
# Importamos la libreria de SQLite
import sqlite3

# Creamos la conexion
connection = sqlite3.connect('test.db')

# Creamos el cursor
cursor = connection.cursor()

# Creamos el arreglo que contiene toda la informacion
datos = [
('Pedro Perez', 34, 'pperez@tucorreo.com', '', 4),
('Maria Gomez', 25, 'maria@sucorreo.com', '', 7),
('Pablo Rodriguez', 41, 'pablor@elcorreo.com', 'www.pablo.com', 3),
]

# Insertamos todos los registros
for t in datos:
cursor.execute('INSERT INTO Usuarios (nombre,edad,correo,url,visitas) VALUES (?,?,?,?,?)', t)

# Hacemos efectiva la transaccion
connection.commit()

# Imprimimos todos los registros
print "\nLista de todos los registros de la base de datos:"
cursor.execute('SELECT * FROM Usuarios')
for row in cursor:
print row

# Imprimimos solo el registro que tenga id = 1
id = (1, )
cursor.execute("SELECT nombre, visitas FROM Usuarios WHERE id=?", id)
for row in cursor:
print "\n%s ha realizado %i visitas" % (row[0], row[1])

# Actualizamos la edad de Pablo
values = (24, 'pablor@elcorreo.com', )
cursor.execute("UPDATE Usuarios SET edad=? WHERE correo=?", values)
connection.commit()
print "\nActualizada la edad de Pablo"

# Y volvemos a imprimir todos los registros para verificar los cambios
print "\nRegistros de la base de datos despues de actualizar a Pablo:"
cursor.execute('SELECT * FROM Usuarios')
for row in cursor:
print row

# Borramos todos los registros con edades mayores de 34 anios
value = (34,)
cursor.execute("DELETE FROM Usuarios WHERE edad >= ?", value)
connection.commit()
print "\nBorrados todos los ancianos ;)"

# E imprimimos otra vez todos los registros para verificar los cambios
print "\nRegistros de la base de datos despues de borrar a los viejitos:"
cursor.execute('SELECT * FROM Usuarios')
for row in cursor:
print row

# Finalmente cerramos todo como debe ser
cursor.close()
connection.close()


Es todo, espero que les haya sido de utilidad este pequeño tutorial

8 comentarios:

William Yánez dijo...

Muy interesante y práctico! espero que pronto le demos alguna utilidad y también te sugiero que lo publiques en Ruby... Saludos!

Anónimo dijo...

muy lindo...sobretodo la letra minima en fondo negro y los ejemplos en letra tamaño 5....imposible de leer...una lectura a un libro de diseño web no caeria mal

Daniel dijo...

Sqlitebrowser está llenísimo de bugs (hasta tal punto que las operaciones normales sobre una sqlite me dan error), por no decir que es inusable. Sqliteman es mucho mejor, pero desafortunadamente no hay paquete en los Repositorios Debian. De todas formas, hay uno en Ubuntu, que es compatible con Debian.

ALex dijo...

Muchas gracias por la información! Tengo que hacer unas prácticas de clase y estaba un poco perdida con sqlite.
Un saludo

TheCharlie´s dijo...

Puedes crear la tabla con este comando:

cursor.execute("""create table if not exists Usuarios (nombre text,edad numeric,correo text,url text,visitas numeric)""")

despues de crear el cursor...

Anónimo dijo...

Un consejo, en lugar de colocar el for para insertar varios registros se puede usar executemany en lugar de execute.

Anónimo dijo...

NO TE IMAGINAS COMO ME SALVASTE AMIGO, MUCHISIMAS GRACIAS!

Jonathan Morales Salazar dijo...

hola, estoy usando ubuntu 16.04 y no logro hacerlo funcionar, me dice que el atributo connect no está definido y me trata de buscar un archivo llamado sqlite3.py ¿alguna idea?