Es frecuente en los foros de Python consultas del tipo,

quiero hacer una consulta a una base de datos utilizando Python, lo que hago és: sql = "select * from table where name =" + myvariable cur.execute(sql) donde myvariable es joe's  cómo lo hago?
La consulta en sí es sencilla y las soluciones que normalmente se dan pasan por aconsejar que se escape la variable para no tener problemas con las comillas y soluciones por el estilo.  Sin embargo, constestaciones de este tipo esconden el problema de base: el sql no debe montarse así, o nos encontraremos con situaciones dignas de xkcd. Vayamos por partes: Python tiene un interfaz común de bajo nivel, la DB-API que es el mínimo denominador común para el acceso a distintas bases de datos. Una de las funciones de la DB-API es evitar en lo posible los problemas derivados de la inyección de código, de modo que la sentencia execute permite el paso de parámetros y éstos serán escapados antes de la generación del código sql que se enviará a la base de datos. La especificación de la DB-API, permite a los desarrolladores de las librerías utilizar diversos medios para el paso de parámetros, debemos conocer cuál utiliza nuestra librería y emplearlo. Por ejemplo: sql = "select * from table where name ?" cur.execute(sql, myvariable) # otro método sql = "select * from table where name=%(name)s" cur.execute(sql, {'name':myvariable}) Una vez que obtengamos los resultados con fetch(one/many/all) todavía nos quedará tratar con los resultados y pasarlos a objetos Python y un problema añadido, que con el * no controlamos en qué orden nos aparecerán los campos. Primera cosa entonces:  Los parámetros no se añaden directamente a la cadena sql, se deja su posición y se pasan dentro de la sentencia execute. Hay pocas razones para violar esta regla y ninguna de ellas es válidas si no somos conscientes de los problemas. La razón de "no va a pasar nada", "a mi me es más fácil así", no sirven, lo siento. Por otro lado también debemos preguntarnos si realmente hay una necesidad de escribr el sql "a pelo". Actualmente existen gran cantidad de libererías que nos permiten olvidarnos del sql y que pasan los resultados directamente a objetos Python, son lo ORM. Los ORM:
  • Nos independizan de la base de datos y de la implementación de la DB-API de nuestra librería.
  • Nos permiten trabajar con objetos en lugar de con sentencias sql, aunque si es necesario nos permiten bajar al nivel de sql que queramos.
  • Escapan los parámetros, evitando ataques de inyección de código.
  • Devuelven objetos Python, ahorrándonos la tediosa conversión de tuplas sql a objetos.
Hay una gran cantidad de ORM para todos los gustos: El ORM de Django, Storm, Autum, SqlRelay, SqlAlchemy, Elixir.  Son algunos de los más populares. Si queremos algo sencillo Autum o Storm nos pueden servir, si estamos trabajando con Django, ya tenemos acceso directamente al ORM y si queremos el ORM más completo y potente del panorama Python SqlAlchemy, complementado o no con Elixir. Los ORM nos fuerzan a definir primero nuestra estructura de objetos que mapearan contra la BD, pero normalmente la sobrecarga que supone esto es mínima comparada con la mantenibilidad y potencia que nos dan. Por ejemplo, la misma sentencia en Django: reg = Table.objects.get(name=myvariable) Donde Table representa el objeto que hemos mapeado contra la estructura de la tabla. El orm ya se encargaría de escapar el contenido de la variable y de generar el sql válido para nuestra base de datos. De regalo en reg tenemos un objeto cuyas propiedades son los campos de la base de datos. Así pues, la recomendación principal es la ir eliminando posibilidades hasta encontrar una que encaje en nuestro problema:
  1. Utilizar un ORM
  2. Utilizar el ORM a bajo nivel para acceder a la API de la libería de base de datos
  3. Utilizar el execute con parámetros
  4. Montar nosotros la sentencia sql escapando siempre los parámetros.
blog comments powered by Disqus