Este artículo está dedicado a aquellas personas que quieran dar sus primeros pasos en el desarrollo de aplicaciones web, y para aquellos que desarrollan aplicaciones sencillas, sin demasiadas complicaciones. Antes de redactar este artículo he estado probando y evaluando diversos frameworks para crear sitios web, tales como
Bobo,
Web2Py,
CherryPy,
Django, etc., pero al final me he decantado por
WebPy, por su extrema sencillez para empezar, y por la similitud que tiene con otros frameworks de otros lenguajes, con lo que la curva de aprendizaje es óptima.
Más adelante, si los proyectos web se vuelven muy complejos y se requiere de elementos prefabricados y avanzados (como objetos persistentes a bases de datos, encriptación, modelo vista controlador, etc.), recomiendo utilizar los otros frameworks citados anteriormente (hay muchos más, pero éstos son más populares y potentes).
Se asume que el lector tiene conocimientos básicos de
Python y que previamente tiene instalado el intérprete de
Python en su máquina:
Configuración del servidor Web
WebPy, a diferencia de otros frameworks, no posee un servidor de aplicaciones potente propio, y por ello, es mejor apoyarse en uno que ya esté funcionando. Entre los posibles candidatos se encuentran
Apache,
Lighttpd y
nginx. Para este artículo he optado por
Apache, dada su gran popularidad.
Una vez instalado
Apache en nuestra máquina, es necesario agregar el módulo
mod_wsgi, que permite hacer uso de Apache como servidor para aplicaciones escritas en
Python. Este proyecto está alojado en:
http://code.google.com/p/modwsgi
Una vez descargado, si estamos usando
Windows, renombrar el archivo a
mod_wsgi.so y copiarlo al directorio
modules de Apache. En el caso de
Linux, compilar el módulo
mod_wsgi (consultar:
http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide)
Una vez instalado el módulo, hay que configurar Apache para que reconozca dicho módulo. Para ello, editar el archivo
httpd.conf (en el directorio
conf de Apache), y agregar la siguiente línea en la misma parte en que Apache utiliza otras cargas
LoadModule:
LoadModule wsgi_module modules/mod_wsgi.so
Al arrancar Apache desde la terminal debería dar un mensaje similar a éste:
Apache/2.2.2 (Unix) mod_wsgi/1.0 Python/2.3 configured
Si se lanzó desde Windows, el Apache Service Monitor, debería dar el mensaje en la parte inferior:
Instalación de WebPy
Hay varias maneras de instalar
WebPy (consultar
http://webpy.org/download).
Se puede descargar directamente la última versión desde la siguiente URL (consultar
http://webpy.org/download para conocer cuál es la última versión):
http://webpy.org/static/web.py-0.34.tar.gz
También se puede descargar desde el repositorio en
github:
http://github.com/webpy/webpy
Una vez descargado, descomprimir el archivo
tar.gz, y ejecutar la siguiente sentencia para instalarlo como librería en
Python:
python setup.py install
Primera aplicación Web
Para ilustrar la sencillez de desarrollo de una página Web con
WebPy, vamos a crear la típica aplicación
“Hola, Mundo”.
import web
urls = (
'/', 'index'
)
app = web.application(urls, globals())
class index:
def GET(self):
return "Hola, Mundo"
if __name__ == "__main__":
app.run()
La variable
urls es una
tupla que contiene pares de direccionamientos URL y nombres de clase. En este ejemplo, el direccionamiento
“/” (
root), estará asociado a la clase
index. Se pueden definir tantos pares como sean necesarios para la aplicación Web.
El objeto
app contendrá la definición de la aplicación Web. En este ejemplo se asocian las URL’s de la aplicación a namespace global.
La clase
index contiene un método
GET, el cual se lanzará cuando se invoque a la página, pudiendo recibir parámetros. En su interior, retornará el literal
“Hola, Mundo”, el cual se imprimirá en el navegador. Existe también un método
POST, que funciona como
GET, pero ofuscando los parámetros. Estos dos métodos son los medios habituales de invocar a una página Web pasando (o no) parámetros.
Por último, las dos últimas líneas indican a
WebPy que comience a servir páginas web con la configuración indicada.
Una vez se ejecuta este programa, el intérprete de
Python se queda en modo de espera. El servicio Web está en marcha, esperando peticiones. Para probarlo, abrimos un navegador web e introducimos la URL:
http://localhost:8080
Debería aparecer nuestro bien conocido mensaje
“Hola, Mundo”.
Parámetros y HTML
En una aplicación Web introduciremos elementos HTML para formatear el aspecto de las páginas. Además, añadiremos dinamismo permitiendo que las páginas muestren datos parametrizados (por ejemplo, recogidos de un formulario, o capturados de una base de datos).
Vamos a añadir más funcionalidad a nuestra aplicación anterior:
import web
urls = (
'/hola', 'Saludo'
)
app = web.application(urls, globals())
class Saludo:
def GET(self):
data = web.input()
return """
<html>
<head></head>
<body background='#AAAAFF'>
<h1>Hola, %s %s %s</h1>
</body>
</html>""" % (data.nombre, data.apellido1, data.apellido2)
if __name__ == "__main__":
app.run()
El primer cambio lo hemos realizado en la sección de
urls, donde ahora, el mapeo a la página Web será el siguiente:
http://localhost:8080/hola
Cuando se apunta a
/hola, se invocará a la clase
Saludo, concretamente a su método
GET, en donde hemos agregado varios cambios. El primero de ellos ha sido definir una variable
data, la cual recogerá los parámetros que se pasen por URL, lo que se consigue mediante la función
input() del módulo web. Estos parámetros se recogerán en formato de diccionario, y son pares de parámetro y valor.
A la hora de retornar la salida, se retornará HTML, en donde hemos añadido un mensaje con formato de título (
<h1>), un saludo con nombre y apellidos (extraídos de los parámetros pasados), los cuales se formatean mediante
%s, e indicando después qué parámetros extraer.
Para invocar a la nueva página se pasan los parámetros en la misma URL, tal y como haría una llamada desde un formulario:
http://localhost:8080/hola?nombre=Carmen&apellido1=Martinez&apellido2=Salguero
El resultado es el siguiente:
Plantillas
En el último ejemplo comenzamos a utilizar HTML, que, al fin y al cabo, es el medio por el cual presentaremos la información en el navegador. El problema que tenemos es que se mezcla el código de
Python y de
HTML, y a la hora de depurar o realizar modificaciones, se vuelve enfarragoso y complejo. La forma ideal de trabajar sería separando la
presentación (HTML) del
código de negocio (el que procesa la información (Python)). Por un lado estarían las etiquetas
HTML que ponen la información, y por otro el código
Python que accede a la base de datos y la prepara en estructuras de datos (por ejemplo).
WebPy posee la característica de utilizar
plantillas, que son directivas especiales embebidas en un código HTML, y que permiten conmutar valores que se pasan desde la capa de negocio o interactuar directamente con código
Python.
Para estructurar nuestro código, vamos a crear un directorio donde se van a guardar las plantillas que vamos a necesitar. En nuestro caso la llamaremos
plantillas. Dentro de este directorio crearemos un fichero llamado (por ejemplo)
plantilla.html, y cuyo contenido será el siguiente:
$def with (nombre, apellido)
<HTML>
<HEAD></HEAD>
<BODY>
$if (nombre!='' and apellido!=''):
<H1>Hola, $nombre $apellido</H1>
$else:
<b>ERROR:</b> No ha facilitado el nombre y el apellido
</BODY>
</HTML>
La primera línea
$def with (nombre, apellido)
debe ser la primera de la plantilla, y se encarga de recoger los parámetros enviados desde el código de negocio, los cuales serán presentados en la página HTML.
A continuación se crea el código HTML. Cuando es necesario presentar la información, hemos creado una estructura de control
if para comprobar los datos pasados. En nuestro caso comprobamos si
nombre y
apellido contienen datos, en cuyo caso imprimirá en un título el mensaje
“Hola, “ seguido del nombre y del apellido. Si uno de los dos datos no se ha facilitado, entonces (
else) se mostrará un mensaje de error.
Ha de notarse que anteponiendo el símbolo
$ (dólar) a una línea en
Python, esta se procesará como código
Python. Asimismo, este símbolo también se utiliza dentro del HTML para sustituir una variable por su valor.
Esta plantilla, por sí misma no hace nada. Debe ser asociada a la capa de negocio. Para ello crearemos el código en un fichero situado fuera del directorio
plantillas, justo en el nivel anterior. El código será el siguiente:
import web
urls = (
'/hola', 'Saludo'
)
render = web.template.render('plantillas')
app = web.application(urls, globals())
class Saludo:
def GET(self):
data = web.input()
nombre = data.nombre if 'nombre' in data else ''
apellido = data.apellido if 'apellido' in data else ''
return render.plantilla(nombre, apellido)
if __name__ == "__main__":
app.run()
El objeto
render apuntará al directorio donde se encuentran nuestras plantillas, creando internamente un objeto por cada plantilla encontrada.
En el método
GET de la clase
Saludo, hemos reforzado el código (a prueba de errores) en cuanto a los parámetros. Si se pasa el parámetro especificado, recogerá su valor. Si no se pasa, le asignará una cadena vacía. Los parámetros pasados están en formato diccionario, por lo que es posible averiguar si el parámetro (clave) está dentro del mismo (
‘clave’ in diccionario).
Por último, se retorna lo generado por la plantilla. Para ello se invoca a la plantilla:
render.plantilla(nombre, apellido)
Como se comentó anteriormente, internamente,
WebPy toma las plantillas dentro del directorio al que apuntaba, y generará un objeto accesible por cada una de ellas. A la plantilla le facilitará los parámetros
nombre y
apellido, que serán recogidos por la primera línea de éste (
$def with).
Un ejemplo más complejo
Los ejemplos anteriores han sido muy básicos. Vamos a ver un poco más a fondo cómo podemos aprovechar todo lo anterior. Imaginemos que queremos hacer una consulta a un catálogo de productos. Esta consulta puede ser general (muestra todos los productos), o bien específica (muestra un producto determinado). La solución pasaría por implementar un acceso a base de datos. Pero para no extender demasiado este artículo, vamos a tenerlo completamente en memoria (para más información sobre cómo acceder a bases de datos desde Python, recomiendo ver mi anterior artículo:
http://rafinguer.blogspot.com/2010/10/conceptos-basicos-de-python-y-sqlite.html.
El código de negocio quedaría como sigue:
import web
urls = (
'/ejemplo', 'Consulta'
)
render = web.template.render('plantillas')
app = web.application(urls, globals())
class Consulta:
def GET(self):
criterio = web.input()
# Tuplas de diccionaris con datos de consulta.
# Puede ser una carga de datos de una bbdd
datos = (
{'producto':'Tornillo', 'precio':0.45, 'marca':'Acme'},
{'producto':'Tuerca', 'precio':0.55, 'marca':'Solme'},
{'producto':'Clavo', 'precio':0.30, 'marca':'Calme'},
{'producto':'Remache', 'precio':0.60, 'marca':'Acme'},
{'producto':'Clavija', 'precio':0.95, 'marca':'Acme'},
{'producto':'Grapa', 'precio':0.25, 'marca':'Tecme'}
)
registro=''
if criterio:
for i in range(0,len(datos)):
if datos[i]['producto'] == criterio.producto:
registro = datos[i]
break
if registro =='':
registro = datos
return render.productos(registro)
if __name__ == "__main__":
app.run()
La llamada a la página puede hacerse sin parámetros (mostraría todos los productos), o bien pasando el parámetro
producto, especificando cuál queremos consultar.
En ambos casos, a la hora de invocar a la plantilla, se pasaría la lista de productos o un único producto, que se almacena en la variable
registro.
El código de la plantilla (
productos.html) sería el siguiente:
$def with (registro)
<HTML>
<HEAD></HEAD>
<BODY>
<TABLE BORDER='1'><TR BGCOLOR='#AAAAFF'>
<TD><B>PRODUCTO<B></TD>
<TD><B>PRECIO<B></TD>
<TD><B>MARCA<B></TD></TR>
$if 'producto' in registro:
<TR>
$for i in range(0,len(registro)):
<TD> $registro.values()[i] </TD>
</TR>
$else:
$for i in registro:
<TR>
<TD> $i['producto'] </TD>
<TD> $i['precio'] </TD>
<TD> $i['marca'] </TD>
</TR>
</TABLE>
</BODY>
</HTML>
En el caso de que se pase un único registro (la consulta se hizo por un único producto), éste será en sí un diccionario, por lo que al consultar si tiene una clave llamada
producto, retornará el valor
True. En tal caso, la función
len() retorna el número de pares clave/valor del diccionario. Conociendo este valor, se recorre cada campo del diccionario en el bucle
for y se extrae su valor, visualizándolo en una columna.
En el caso de pasar el catálogo completo, la función
len() retornaría el número de filas (diccionarios) que contiene la lista. El acceso a la lista debe realizarse mediante un iterador (
i), el cual recoge, una a una, cada fila (diccionario). El valor se extrae invocando el nombre de su clave.
El resultado final sería el siguiente:
Conclusiones
Este breve tutorial cubre los aspectos más básicos, pero, a la vez, más potentes y flexibles, para comenzar a desarrollar aplicaciones Web mediante
Python. A partir de las nociones vistas anteriormente, podremos empezar a desarrollar complejas web dinámicas de una forma sencilla y eficaz.
Referencias
Lenguaje Python: http://www.python.org
mod_wsgi: http://code.google.com/p/modwsgi
WebPy: http://webpy.org
Apache: http://httpd.apache.org