May 01

Python con MySQL

 

Hay una tontería de la conexión de python con MySQL que me ha llamado la atención y aprovecho para comentar aquí. El tema es que según obtengamos el cursor de la conexión para hacer las consultas, podemos acceder a los resultados de una manera o de otra.

Si obtenemos el cursor de esta manera

conn = MySQLdb.connect (….)
cursor = conn.cursor()

una vez que hagamos una consulta y obtengamos una de las filas resultado, debemos acceder a cada uno de los campos usando un índice de un array

cursor.execute ("select * from tabla")
fila = cursor.fetchone()
# para acceder al primer campo
print fila[0]  

Sin embargo, al obtener el cursor podemos decir que queremos que las filas sean dictionaries, en vez de tuplas, de manera que podemos acceder a los campos usando el nombre del campo, en vez de un índice. Para ello, basta con obtener el cursor de esta manera

conn = MySQLdb.connect (…)
cursor = conn.cursor(MySQLdb.cursors.DictCursor)

y así podemos acceder a los campos a través de su nombre

cursor.execute("select * from tabla")
fila = cursor.fecthone()
# para acceder a uno de los campos
print fila["nombre_columna"]

Una tontería, pero estoy acostumbrado a java y a C++ y no a lenguajes tan flexibles.

 

Apr 23

Python: Una de cal y una de arena

Sé que "una de cal y una de arena" significa una cosa buena y una cosa mala. El problema es que nunca he sabido si la cal es la mala, la arena la buena o a la viceinversa.

Hace tiempo comenté que iba a hacer una pequeña aplicación web en python para pedirle a la gente que metiera cada mes el tiempo que dedica a cada uno de los proyectos, de forma que luego sacara en excel una tabla con dichos tiempos. Pues bien, ya está hecha (un poco de aquella manera) y funcionando. Así que tras esta mínima experiencia con python, ahí van un par de impresiones, una buena y otra mala, una de cal y otra de arena.

La cosa mala: Me da la impresión, al igual que casi todos los lenguajes de script en los que defines las cosas sobre la marcha, que python es un lenguaje muy difícil de mantener. Al no definirse claramente los tipos, cuando en una función o método recibes parámetros, no tienes ni idea de lo que recibes, salvo que lo pongas muy bien comentado. De hecho, en eclipse con el plugin pydev para programar en python, el autocompletar que te muestra los nombres de atributos y métodos de las clases, eclipse sólo te puede mostrar aquellos atributos y clases que hayas usado previamente en el código.

En java, por ejemplo, hay que declararlo todo, por lo que en cualquier momento sabes cada variable de qué tipo es y qué cosas tiene o a las que puedes llamar. No dependes (salvo para entenderlo mejor) de que el programador se haya acordado de comentar adecuadamente el código.

La cosa buena: Precisamente esta falta de tipado y el poder meter una manzana donde se espera un higo me da la impresión que hace de python un lenguaje muy flexible, y pongo un ejemplo. Puesto que mi aplicación es web, en casi todos los métodos/funciones que he hecho recibo de parámetro un Request Object, que el mismo servidor web se encarga de pasarme y con el que tengo acceso a los parámetros de la petición http, con el que puedo escribir los tag html que se verán en el navegador, etc. Pues bien, para mis pruebas sin servidor web desde eclipse, me hice una clase MiClase con un método write() similar al de Request Object, lo instancié y llamé a mis métodos a pelo pasándoles una instancia de MiClase. El código "tragó" con eso perfectamente, y la salida html salía por donde decía MiClase, es decir, por pantalla normal.

En otros lenguajes como java habría sido necesario heredar del objeto en cuestión y sobreescribir los métodos necesarios, quizás incluso declarar un constructor obligatorio con los parámetros raros que tuviera la clase padre. En java es aconsejable el uso de interfaces precisamente por este motivo, para poder cambiar una cosa por otra sin "cargar" con la clase original heredando de ella. En python no hacen falta interfaces. Basta con que la clase sustituta tenga los métodos que se usen de la clase original.

Todo esto me hace preguntarme si lo del desarrollo rápido de lenguajes como python se refiere a que no es necesario declarar los tipos (desde luego, eso ahorra tiempo, pero me parece un tiempo mínimo respecto a todo el proceso o el tiempo que puedes perder en depuración mientras decides si una variable es de un tipo u otro), o bien se debe a esta flexibilidad del lenguaje, que permite mezclar churras con merinas y todo funciona como debe.

Feb 07

Cosas que me han llamado la atención de la sintaxis de Python

Hay muchas cosas que me han llamado la atención de la sintaxis de python, quizás por haber estado centrado mucho tiempo en lenguajes como C/C++ o java, que son muy similares en cuanto a sintaxis, y haber hecho alguna incursión en php, que también tiene muchas similitudes con C. El basic y el pascal me quedan ya lejanos….

De la sintaxis de python me ha llamado la atención la forma de asignar variables. Si hacemos esto

a,b,c=1,2,3

asigna los valores uno a uno, es decir, la a valdrá 1, la b 2 y la c 3. Es más, si d es una especie de Hastable, -en python creo que se llama defaultdict-, podemos hacer un bucle así

for clave,valor in d:

y se iran asignando automáticamente clave y valor en cada una de las iteraciones. Realmente cómodo si lo comparamos con java, que hay que pedir las keys() y luego iterar y con cada key pedir el get(key) correspondiente.

En cuanto a las clases python, también es curioso el tema de los atributos. Estos, al igual que el resto de las variables, NO se declaran. En el mismo constructor de la clase emprezamos a crearlas sobre la marcha asignándoles valores.

class UnaClase:
   def __init__(self):
      self.unAtributo="hola";
      …

Todos los métodos, el constructor y demás métodos, reciben un parámetro self, equivalente al this, pero lo reciben explicitamente. Además, incluso desde fuera de la clase, cuando la estamos usando, podemos añadirle más atributos sobre la marcha. En el caso de UnaClase, podemos hacer perfectamente esto

a = UnaClase()
print a.unAtributo   # escribe "hola"
a.otroAtributo="adios"
print a.otroAtributo  # escribe "adios"

Y también es realmente curioso que no hay nada similar a abrir y cerrar llaves, ni begin-end, ni cosas de esas. El cuándo empieza o termina un bucle, una definición o cualquier otra cosa …. ¡¡ se hace con el sangrado !!. Bueno, no es totalmente cierto, hay que poner un dos puntos al final de la línea en la que empieza el bucle o definición y las siguientes líneas ponerlas sangradas hasta que queramos terminar el bucle o definición.

Un lenguaje realmente curioso.

Feb 06

Python y formularios

Bueno, no sé si es la solución fetén, pero he encontrado la forma de hacer formularios "dinámicos" y luego recoger los parámetros en una función de Python.

Creo mi formulario con una consulta a base de datos, consultando los proyectos y creando tantos campos <input type="text" name="id_…./>  como proyectos haya. El id_… es un trozo de texto "id_" al que concateno detrás el identificador del proyecto en base de datos. Esto hace que en la página python que recibe el resultado del formulario no se pueda saber a priori los nombres de los parámetros. Y más si tenemos en cuenta que en python debemos crear una función cuyos parámetros sean precisamente esos, los nombres de los parámetros del formulario.

# La función debería ser así….
def funcion (req, id_proyecto1, id_proyecto2, id_proyecto3):

La forma que he encontrado sólo funciona cuando el formulario es tipo GET. La función python que recibe el formulario recibe también un Request Object que tiene datos sobre la petición http. Dicho objeto es el primer parámetro req del trozo de código anterior. Pues bien, si la petición ha sido por GET, entonces req.args contiene el trozo de cadena de la URL con los parámetros. Es decir, algo como "id_xxx=32&id_yyy=3&id_zzz=1" donde xxx, yyy y zzz son los identificadores de los proyectos.

Ahora sólo queda, con un bucle y un par de llamadas a la función split(), separar adecuadamente todo. Por supuesto, siguiendo mi costumbre, he puesto todo esto con un poco más de detalle en recoger parámetros de un formulario con python en la Chuwiki.

Feb 04

Pythoneando

Pues a ratos libres y poco a poco, puesto que lo hago en casa, sigo con mi mini-proyecto python-apache-mysql para los tiempos que dedica la gente a los proyectos. Como a mí me gusta escribir todo lo que aprendo, ahí van un resumen de dos de las pruebas que he hecho hasta ahora.

Al final, puesto que el objetivo último de esta aplicación es generar el excel que luego se enviará a las "altas esferas" de la empresa -los que manejan los dineros-, me he tenido que pasar a Windows. Con python es fácil generar el excel usando win32com, que ya viene instalado con ActivePython. Hay librerías de python para generar excel, como pyExcelerator, pero las veo todavía en un estado muy básico -versión 0.x.x-.

Por cierto, le tengo algo de manía a Windows y por eso no suelo hacer mucho caso de las cosas que tiene, pero me ha llamado la atención esto de los objetos COM -o lo que sea-. Había oído hablar de ellos, pero no sabía muy bien qué era exactamente. Sigo sin saberlo, pero me ha hecho gracia poder ¿conectarme? con python a un ¿servidor de objetos COM?, pedirle una hoja de excel … ¡ y ver cómo se abre el excel !. Luego, según iba escribiendo comandos python para crear la hoja, las celdas y demás…. ¡ el excel se actualiza sobre la marcha !.

Cambiando de tema, ahora tengo un problema estúpido con python que es el que estoy tratando de resolver. Una de las páginas web pide al usuario los días trabajados en cada uno de los proyectos. Para ello, hago una consulta a la base de datos para ver los proyectos que hay y genero el formulario poniendo una etiqueta y campo de texto por cada proyecto, es decir, un

proyecto : <input type="text" name="id_proyecto" … />

por supuesto, el proyecto e id_proyecto lo genero dinámicamente con los ids/nombres de los proyectos leídos de base de datos. Pues bien, mi problema está cuando envío el submit a la página siguiente. En Python la página siguiente es una función cuyos parámetros son los distintos "name" del formulario, pero como son dinámicos, no puedo crear la función. En PHP se resuelve esto mirando algo como la variable $_POST, a la que puedes pedir los nombres de variable y los valores.

En Python he visto algo que dice que se puede hacer con cgi.FieldStorage() que supuestamente te devuelve una especie de "cosa" en la que están todas las variables del formulario. De momento no me ha funcionado….  pero sigo investigando.

Jan 30

Apache con Python en ubuntu

Para empezar a jugar con la mini-aplicación que comenté en el post anterior, me he dedicado a instalar en casa Apache en ubuntu, y luego el módulo de Python.

Lo de Apache sin problemas. Usé el gestor de paquetes de synaptic, busqué apache2 para marcar el servidor Apache y busqué "apache2 python", para el mod de Apache/Python. La instalación sin problemas.

Python viene instalado en ubuntu y MySQL ya lo tenía.

El módulo de Apache/python parece que se carga el solito en la configuración de Apache. El fichero /etc/apache2/apache2.conf hace un include de /etc/apache2/mods-enabled/*.load y ahí dentro está el fichero mod-python.load que se encarga de cargar el módulo.

En el fichero /etc/apache2/apache2.conf añadí al final unas líneas para indicar en qué directorios estarán mis programas python y que Apache sepa redirigir las peticiones al sitio adecuado. Según veo en la ayuda, hay dos posibles formas de hacerlo. Una consiste en indicar un directorio y un handler en python que nos hagamos a medida. La otra  opción, que yo he usado, es poner un handler por defecto. Las líneas añadidas al final del fichero apache2.conf son

<Directory /var/www/python>
        SetHandler mod_python
        PythonHandler mod_python.publisher
</Directory>

De esta forma, cualquier petición a http://localhost/python/cosa se traduce en la llamada a la función cosa() dentro del fichero index.py. También, si tenemos un fichero kk.py con una función hola(), se puede poner http://localhost/python/kk.py/hola. Si simplemente ponemos http://localhost/python/, buscará un index.py y dentro la función index().

Esto es algo que me ha llamado mucho la atención. De php, jsp o html estoy acostumbrado a llamar desde la url directamente a un fichero .php, .jsp o .html. Aquí se llama a la función de dentro del fichero.

Las funciones que ponemos en python reciben un parámetro req. Este es un objeto que contiene la información de la petición hecha por el cliente -ip del cliente, uri pedida, etc- y que a su vez se usa para devolver los resultados al cliente. Algo típico podría ser esto

def funcion(req):
   req.content_type = "text/html"
   req.write("<html><head></head><body><p>hola mundo</p></body></html>")
  

Una vez vistos los rudimentos de cómo va esto y sin tener ni pajolera idea de python, me decidí a intentar conectarme a la base de datos de mysql. Un copy-paste de código encontrado por internet me da esto

def db(req):
   try:
      import MySQLdb
      db=MySQLdb.connect(host=’localhost’,user=’el_user’, passwd=’la_passwd’,db=’la_bd’)
      cursor=db.cursor()
      sql=’Select * From tabla’
      cursor.execute(sql)
      resultado=cursor.fetchall()
      req.content_type="text/html"
      req.write(’Datos de la tabla<br>’)
      for registro in resultado:
         #suponemos tres campos, uno numerico y dos string
         #el numerico necesita conversion a string str(…)
         req.write(str(registro[0])+’,'+registro[1]+’,'+registro[2]+’<br>’)
         req.write(”)
   except:
      return ‘error’

y listo, funcionó tras algunas pruebas y cambios. El return "error" del final es en caso de excepción. Ese texto se mostrará en el navegador tal cual.

Ahora sólo me queda empollar un poco de python.