Existen problemas para los que guardar nuestros datos en ficheros de texto plano, en archivos XML, o mediante serialización con pickle
o shelve
pueden ser soluciones poco convenientes. En ocasiones no queda más remedio que recurrir a las bases de datos, ya sea por cuestiones de escalabilidad, de interoperabilidad, de coherencia, de seguridad, de confidencialidad, etc.
A lo largo de este capítulo aprenderemos a trabajar con bases de datos en Python. Sin embargo se asumen una serie de conocimientos básicos, como puede ser el manejo elemental de SQL. Si este no es el caso, existen miles de recursos a disposición del lector en Internet para introducirse en el manejo de bases de datos.
DB API
Existen cientos de bases de datos en el mercado, tanto comerciales como gratuitas. También existen decenas de módulos distintos para trabajar con dichas bases de datos en Python, lo que significa decenas de APIs distintas por aprender.
En Python, como en otros lenguajes como Java con JDBC, existe una propuesta de API estándar para el manejo de bases de datos, de forma que el código sea prácticamente igual independientemente de la base de datos que estemos utilizando por debajo. Esta especificación recibe el nombre de Python Database API o DB-API y se recoge en el PEP 249 (http://www.python.org/dev/peps/pep-0249/).
DB-API se encuentra en estos momentos en su versión 2.0, y existen implementaciones para las bases de datos relacionales más conocidas, así como para algunas bases de datos no relacionales.
A lo largo de este capítulo utilizaremos la base de datos SQLite para los ejemplos, ya que no se necesita instalar y ejecutar un proceso servidor independiente con el que se comunique el programa, sino que se trata de una pequeña librería en C que se integra con la aplicación y que viene incluida con Python por defecto desde la versión 2.5. Desde la misma versión Python también incorpora un módulo compatible con esta base de datos que sigue la especificación de DB API 2.0: sqlite3
, por lo que no necesitaremos ningún tipo de configuración extra.
Nada impide al lector, no obstante, instalar y utilizar cualquier otra base de datos, como MySQL, con la cuál podemos trabajar a través del driver compatible con DB API 2.0 MySQLdb
(http://mysql-python.sourceforge.net/).
Variables globales
Antes de comenzar a trabajar con sqlite3
, vamos a consultar algunos datos interesantes sobre el módulo. Todos los drivers compatibles con DB-API 2.0 deben tener 3 variables globales que los describen. A saber:
apilevel
: una cadena con la versión de DB API que utiliza. Actualmente sólo puede tomar como valor «1.0» o «2.0». Si la variable no existe se asume que es 1.0.threadsafety
: se trata de un entero de 0 a 3 que describe lo seguro que es el módulo para el uso con threads. Si es 0 no se puede compartir el módulo entre threads sin utilizar algún tipo de mecanismo de sincronización; si es 1, pueden compartir el módulo pero no las conexiones; si es 2, módulos y conexiones pero no cursores y, por último, si es 3, es totalmente thread-safe.paramstyle
: informa sobre la sintaxis a utilizar para insertar valores en la consulta SQL de forma dinámica.- qmark: interrogaciones.
sql = «select all from t where valor=?» - numeric: un número indicando la posición.
sql = «select all from t where valor=:1» - named: el nombre del valor.
sql = «select all from t where valor=:valor» - format: especificadores de formato similares a los del printf de C.
sql = «select all from t where valor=%s» - pyformat: similar al anterior, pero con las extensiones de Python.
sql = «select all from t where valor=%(valor)»
- qmark: interrogaciones.
Veamos los valores correspondientes a sqlite3:
>>> import sqlite3 as dbapi >>> print dbapi.apilevel 2.0 >>> print dbapi.threadsafety 1 >>> print dbapi.paramstyle qmark
Excepciones
A continuación podéis encontrar la jerarquía de excepciones que deben proporcionar los módulos, junto con una pequeña descripción de cada excepción, a modo de referencia.
StandardError
|__Warning
|__Error
|__InterfaceError
|__DatabaseError
|__DataError
|__OperationalError
|__IntegrityError
|__InternalError
|__ProgrammingError
|__NotSupportedError
StandardError
: Super clase para todas las excepciones de DB API.Warning
: Excepción que se lanza para avisos importantes.Error
: Super clase de los errores.InterfaceError
: Errores relacionados con la interfaz de la base de datos, y no con la base de datos en sí.DatabaseError
: Errores relacionados con la base de datos.DataError
: Errores relacionados con los datos, como una división entre cero.OperationalError
: Errores relacionados con el funcionamiento de la base de datos, como una desconexión inesperada.IntegrityError
: Errores relacionados con la integridad referencial.InternalError
: Error interno de la base de datos.ProgrammingError
: Errores de programación, como errores en el código SQL.NotSupportedError
: Excepción que se lanza cuando se solicita un método que no está soportado por la base de datos.
Uso básico de DB-API
Pasemos ahora a ver cómo trabajar con nuestra base de datos a través de DB-API.
Lo primero que tendremos que hacer es realizar una conexión con el servidor de la base de datos. Esto se hace mediante la función connect
, cuyos parámetros no están estandarizados y dependen de la base de datos a la que estemos conectándonos.
En el caso de sqlite3
sólo necesitamos pasar como parámetro una cadena con la ruta al archivo en el que guardar los datos de la base de datos, o bien la cadena ":memory:"
para utilizar la memoria RAM en lugar de un fichero en disco.
Por otro lado, en el caso de MySQLdb
, connect
toma como parámetros la máquina en la que corre el servidor (host
), el puerto (port
), nombre de usuario con el que autenticarse (user
), contraseña (password
) y base de datos a la que conectarnos de entre las que se encuentran en nuestro SGBD (db
).
La función connect
devuelve un objeto de tipo Connection
que representa la conexión con el servidor.
>>> bbdd = dbapi.connect("bbdd.dat") >>> print bbdd <sqlite3.Connection object at 0x00A71DA0>
Las distintas operaciones que podemos realizar con la base de datos se realizan a través de un objeto Cursor
. Para crear este objeto se utiliza el método cursor()
del objeto Connection
:
c = bbdd.cursor()
Las operaciones se ejecutan a través del método execute
de Cursor
, pasando como parámetro una cadena con el código SQL a ejecutar.
Como ejemplo creemos una nueva tabla empleados en la base de datos:
c.execute("""create table empleados (dni text, nombre text, departamento text)""")
y a continuación, insertemos una tupla en nuestra nueva tabla:
c.execute("""insert into empleados values ('12345678-A', 'Manuel Gil', 'Contabilidad')""")
Si nuestra base de datos soporta transacciones, si estas están activadas, y si la característica de auto-commit está desactivada, será necesario llamar al método commit
de la conexion para que se lleven a cabo las operaciones definidas en la transacción.
Si en estas circunstancias utilizáramos una herramienta externa para comprobar el contenido de nuestra base de datos sin hacer primero el commit
nos encontraríamos entonces con una base de datos vacía.
Si comprobáramos el contenido de la base de datos desde Python, sin cerrar el cursor ni la conexión, recibiríamos el resultado del contexto de la transacción, por lo que parecería que se han llevado a cabo los cambios, aunque no es así, y los cambios sólo se aplican, como comentamos, al llamar a commit
.
Para bases de datos que no soporten transacciones el estándar dicta que debe proporcionarse un método commit
con implementación vacía, por lo que no es mala idea llamar siempre a commit
aunque no sea necesario para poder cambiar de sistema de base de datos con solo modificar la línea del import
.
Si nuestra base de datos soporta la característica de rollback
también podemos cancelar la transacción actual con:
bbdd.rollback()
Si la base de datos no soporta rollback llamar a este método producirá una excepción.
Veamos ahora un ejemplo completo de uso:
import sqlite3 as dbapi bbdd = dbapi.connect("bbdd.dat") cursor = bbdd.cursor() cursor.execute("""create table empleados (dni text, nombre text, departamento text)""") cursor.execute("""insert into empleados values ('12345678-A', 'Manuel Gil', 'Contabilidad')""") bbdd.commit() cursor.execute("""select * from empleados where departamento='Contabilidad'""") for tupla in cursor.fetchall(): print tupla
Como vemos, para realizar consultas a la base de datos también se utiliza execute
. Para consultar las tuplas resultantes de la sentencia SQL se puede llamar a los métodos de Cursor
fetchone
, fetchmany
o fetchall
o usar el objeto Cursor
como un iterador.
cursor.execute("""select * from empleados where departamento='Contabilidad'""") for resultado in cursor: print tupla
El método fetchone
devuelve la siguiente tupla del conjunto resultado o None
cuando no existen más tuplas, fetchmany
devuelve el número de tuplas indicado por el entero pasado como parámetro o bien el número indicado por el atributo Cursor.arraysize
si no se pasa ningún parámetro (Cursor.arraysize
vale 1 por defecto) y fetchall
devuelve un objeto iterable con todas las tuplas.
A la hora de trabajar con selects u otros tipos de sentencias SQL es importante tener en cuenta que no deberían usarse los métodos de cadena habituales para construir las sentencias, dado que esto nos haría vulnerables a ataques de inyección SQL, sino que en su lugar debe usarse la característica de sustitución de parámetros de DB API.
Supongamos que estamos desarrollando una aplicación web con Python para un banco y que se pudiera consultar una lista de sucursales del banco en una ciudad determinada con una URL de la forma http://www.mibanco.com/sucursales?ciudad=Madrid
Podríamos tener una consulta como esta:
cursor.execute("""select * from sucursales where ciudad='" + ciudad + "'""")
A primera vista podría parecer que no existe ningún problema: no hacemos más que obtener las sucursales que se encuentren en la ciudad indicada por la variable ciudad
. Pero, ¿qué ocurriría si un usuario malintencionado accediera a una URL como «http://www.mibanco.com/sucursales?ciudad=Madrid’;SELECT * FROM contrasenyas»?
Como no se realiza ninguna validación sobre los valores que puede contener la variable ciudad, sería sencillo que alguien pudiera hacerse con el control total de la aplicación.
Lo correcto sería, como decíamos, utilizar la característica de sustitución de parámetros de DB API. El valor de paramstyle
para el módulo sqlite3
era qmark
. Esto significa que debemos escribir un signo de interrogación en el lugar en el que queramos insertar el valor, y basta pasar un segundo parámetro a execute
en forma de secuencia o mapping con los valores a utilizar para que el módulo cree la sentencia por nosotros.
cursor.execute("""select * from sucursales where ciudad=?""", (ciudad,))
Por último, al final del programa se debe cerrar el cursor y la conexion:
cursor.close() bbdd.close()
Tipos SQL
En ocasiones podemos necesitar trabajar con tipos de SQL, y almacenar, por ejemplo, fechas u horas usando Date y Time y no con cadenas. La API de bases de datos de Python incluye una serie de constructores a utilizar para crear estos tipos. Estos son:
Date(year, month, day)
: Para almacenar fechas.Time(hour, minute, second)
: Para almacenar horas.Timestamp(year, month, day, hour, minute, second)
: Para almacenar timestamps (una fecha con su hora).DateFromTicks(ticks)
: Para crear una fecha a partir de un número con los segundos transcurridos desde el epoch (el 1 de Enero de 1970 a las 00:00:00 GMT).TimeFromTicks(ticks)
: Similar al anterior, para horas en lugar de fechas.TimestampFromTicks(ticks)
: Similar al anterior, para timestamps.- Binary(string): Valor binario.
Otras opciones
Por supuesto no estamos obligados a utilizar DB-API, ni bases de datos relacionales. En Python existen módulos para trabajar con bases de datos orientadas a objetos, como ZODB (Zope Object Database) y motores para mapeo objeto-relacional (ORM) como SQLAlchemy, SQLObject o Storm.
Además, si utilizamos IronPython en lugar de CPython tenemos la posibilidad de utilizar las conexiones a bases de datos de .NET, y si utilizamos Jython, las de Java.
Excelente post, hace ya un tiempo escribí un post en la comunidad SQLite-Latino sobre Python y SQLite. Les dejo el enlace si es que quieren revisarlo.
http://sqlite-latino.blogspot.com/search/label/SQLite%20y%20Python
Saludos
Muy bueno.!
Ya que comentas sobre XML para guardar datos… ¿Qué librerías utilizas? ¿Prefieres Databinding o manipulación de DOM?
Ah, y gracias por tus estupendos artículos.
quisiera saber si a una bd sqlite se le puede poner contraseña y como, gracias
@Esteban, que yo sepa no. Lo que si existe es una extensión que permite cifrarlas, pero costaba bastante.
WoW. Cada que leo un post de python me alegro de haber encontrado este blog.
Muchas gracias.!!!
Por cierto. Habra una actualizacion del manual de python para todos? 😀
Gracias nuevamente!!!
Claro, lo actualizaré con lo nuevo cuando tenga tiempo.
Yo uso actualmente sqlite3, pero me llama la atencion sqlalchemy por que la sintaxis no queda atada al tipo de base. Pero no tengo una razón firme o la ventajas y desventajas que traería usar alchemy. Si me pueden sacar de dudas les agradezco.
Mu bueno, thx ^^
Esta muy bueno, me ayudo un poco. Pero sabes como evaluar el resultado que se toma con una sentencia ‘if’ no logro hacerlo
Hola, quería consultarte si es viable que nos puedan inyectar código SQL???
@hugo si no compruebas las entradas de los usuarios, si claro.
Okis, gracias. Me fije y lo que guardo en la bb.dd usando la conexión como la explicaste mas arriba no tiene problemas, ya que los parámetros que guardo los genero yo, así que es imposible que un usuario pueda inyectarme código malicioso. Saludos
Pingback: MySQL
si tengo funciones implementadas en postgresSQL como desde python puedo ejecutarlas.
hola quisiera que me ayudara a hacer una base de datos en python pero es que en realidad no se como hacerla y quisiera de su gran ayuda pues tal vez con un ejemplo o algo que viera que me pudira ser de gran ayuda, pues el programa que me pidieron hacer es que captura yo la calificacion de 3 alumnos y tambien que ese mismo programa modifique,elimine, datos que yo le haya agregado en fin un sin fin de cosas.
espero y me pueda ayudar y si quisira mandarme algo a mi correo.
adios y espero su pronta respuesta.
att: una estudiante desesperada
hola, quisiera que me ayudara a hacer una base de datos en python,si quieres que te sea sincero no se como empezarla. mi base de datos ha de contener informacion, que solo puede ser vista por mi, como administrador… La base ha de contener informacion de los diferentes usuarios que se quieran inscribir en un tipo de chat, con la opcion de tener amistad, poder bloquear contactos… y esto mediante el Dni como informacion principal, y luego cada usuario tendria su perfil: con el nombre, edad,…
me gustaria si pudiera ser una pequeña introduccion de como puedo empezar el trabajo o un ejemplo para poder visualizar como tengo que trabajar y asin poderme basar en algo.
un saludo, si me has de enviar algo en mi correo. gracias
aaaaaaaaaa y tambien como importar base de datos y archivos en python.
Hola, está muy bien el tutorial. Yo tengo el siguiente problema. Tengo una BBDD en Python y quiero que no me dé error cuando hago una consulta vacía. ¿Existe alguna forma? De hecho, quiero aprovechar si la consulta no devuelve nada para hacer determinadas acciones.
Gracias por adelantado.
todo lo que mencionas en el post, esta en el libro de python para todos, que bueno que te tomaste el tiempo para postearlo, pero debes dar el credito a el autor del libro… de lo contrario se pensaria que tu eres el autor…
mm pido disculpas … por el comentario anterior donde dije que deberias poner al autor del libro, navegando por internet llegue a tu primer post donde explicas que todos tus post estan basados en el libro, repito disculpas por lo que dije anteriormente… de paso te pregunto que si conoces algun libro dirigido a aplicaciones para windows con python
No pasa nada databack. Ya me ha pasado otras veces 😛
Sobre programación para Windows, puedes echar un vistazo, por ejemplo, a Python Programming On Win32, de O’Reilly.
que chistoso, muchos aun piensan que te pirateas cosas del libro. siendo que tu lo escribiste xD…
que extensión tiene la base de datos sqlite3
oye muy buen articulo. me podrias pasar la bibliografia del libro que usaste. gracias
El artículo es parte de mi libro Python para todos. Es gratuito y Creative Commons 🙂
muy bueno¡¡¡ su pagina pero por favor me podrian ayudar en lo k es un programita en una creacion de pasajeros,boletos,conductor,unidades,usuarios por favor¡¡¡¡¡necesito zi graxias
Le recomiendo que ponga en cada post los créditos de los autores. Piense que muchas personas buscan una información concreta y el buscador les lleva directamente a la página en cuestión y no tienen porque acceder al lugar donde tenga los datos de los autores.
Por respeto al trabajo de los autores debe aparecer al comienzo de cada post. No cuesta ningún esfuerzo y reconocerá el trabajo de los demás como corresponde.
Muchas gracias.
El autor soy yo. Por favor, se agradecería que leyerais los comentarios antes de escribir uno.
hola, Soy nuevo en python, como puedo configurar o cual administrador de las db del sqlite3. Esto es posible , ejemplo como el HeidiSQL para mysql, o el crea la db como un archivo y ya.
Gracias de antemano.
ya encontré algunas opciones como http://damncorner.blogspot.com/2009/04/como-usar-sqlite-en-python.html y navicat , ahora mi pregunta es la siguiente como puedo realizar una aplicaciones web en python , puedo utilizar sqlite o debe utilizar mysql , y que servidor web puedo instalar para hacer pruebas.
gracias
Hermano, soy un fiel seguidor de tus publicaciones y del libro, me gustaria saber, como puedo manipular un dato numerico en la bd para, por ejemplo, sumarle 1, de antemano gracias
hola, quisiera que me ayudara a hacer una base de datos en python,si quieres que te sea sincero no se como empezarla. mi base de datos ha de contener informacion, que solo puede ser vista por mi, como administrador… La base ha de contener informacion de los diferentes usuarios que se quieran inscribir en un tipo de chat, con la opcion de tener amistad, poder bloquear contactos… y esto mediante el Dni como informacion principal, y luego cada usuario tendria su perfil: con el nombre, edad,…
me gustaria si pudiera ser una pequeña introduccion de como puedo empezar el trabajo o un ejemplo para poder visualizar como tengo que trabajar y asin poderme basar en algo.
un saludo, si me has de enviar algo en mi correo. gracias
hola he estado intentando mapear una bd de oracle con sqlalchemy como lo hace java con hibernate que te genera de las tablas clases no se si me hago entender bueno la cuestion es que me genera error porque al ejecutar el script me dice que la fk tiene el mismo nombre que la tabla eso ya lo corregi ahora me sale otro error que no puede mapear ya que un disparador que se creo en la db hace herencia a una tabla como podria solucionar este problema ya que no se como utilizar bien sqlalchemy y pues la documentacion no ayuda sobre aquel problema otro pregunta es este orm me genera los archivos como java+hibernate? espero un poco de ayuda gracias
Pingback: XML dentro de Bases de datos… | Sangre de Diablo Blog