pensando en python (iii): 3 en raya en la...

8
SOLO PROGRAMADORES nº 120 Introducción Dada la popularidad de Internet, el desarrollo de apli- caciones con interfaz web, agnósticas de la platafor- ma donde se ejecute el cliente web (navegador), ha experimentado un crecimiento exponencial. A pesar de la menor sofisticación de las interfaces web res- pecto a las interfaces gráficas de aplicaciones de sobremesa, su facilidad tanto de uso, dada la familia- ridad de los usuarios con el paradigma web, como su simplicidad de desarrollo, son razones suficientes para considerarlas como primera opción en cualquier desarrollo que una empresa acomete hoy en día. En las aplicaciones web, una sola instancia de la misma ejecutándose en un servidor web (o varias en un cluster de servidores), es compartida por varios (en algunos casos cientos o miles) de usuarios que acce- den a ella desde un cliente universal, el navegador de Internet. El principal cometido de una aplicación web es crear páginas HTML (o en cualquier otro lenguaje de marcado) de manera dinámica. Es decir, los datos estáticos predefinidos de una página son combinados con datos generados por la lógica de la aplicación u obtenidos de fuentes de datos en tiempo real, por ejemplo de una base de datos. Durante la ejecución concurrente de las tareas requeridas simultáneamen- te por los diferentes usuarios de la aplicación web es frecuente que se detecten conflictos de acceso y modificación a datos en el servidor. Además, la aplica- ción web debe generar las páginas web, ejecutando toda la lógica de negocio subyacente, de la manera más rápida y eficiente posible. Es por tanto necesario, adoptar mecanismos en la parte servidora que agili- cen los accesos a datos y soporten transacciones, es decir, conjuntos de operaciones físicas relacionadas sobre datos, que aparecen al cliente como una unidad lógica. Entran en juego aquí los llamados sistemas gestores de bases de datos (SGBD). La versión más comúnmente utilizada de los mismos son los sistemas relaciones de bases de datos. En definitiva, servidores web y bases de datos relacionales conforman un bino- mio esencial en la creación de toda aplicación web. A continuación, explicaremos cómo se pueden crear aplicaciones web en Python que acceden a bases de datos relacionales. Como resultado de este proceso transformaremos la aplicación de sobremesa tres en raya desarrollada durante las dos anteriores entregas en una aplicación web. Programación web en Python Al tratarse Python de un lenguaje de código abierto existen multitud de módulos/librerías para realizar cualquier tarea programática que imaginemos. En ocasiones, como es el caso de la programación web, existen varios módulos diferentes que de una mane- ra más básica o sofisticada nos permiten llevar a cabo la misma tarea. Para comprobarlo no tenemos más que visitar el portal de Python, en su sección de Temas/web (http://www.python.org/topics/web/). Allí podemos encontrar un largo listado de estos módu- los, detallando sus diferentes funciones. A continua- ción enumeramos los más destacados, clasificados según la categoría de plataforma de desarrollo de aplicación web a la que pertenecen: Programación CGI Básica: Módulo CGI de la librería Standard de Python. CGI (Common Gateway Inteface) es un mecanismo estándar para la ejecución de código ejecutable por un servidor web y la obtención de los resultados de tal ejecución. Módulo Cookie para la creación y procesa- miento de cookies en aplicaciones web. Una cookie es un mecanismo para mantener esta- do entre las peticiones HTTP de una sesión web. Una cookie es una cabecera HTTP que permite la identificación unívoca en el servi- dor web del peticionario de la información. Programación CGI Avanzada. En CGI, un nuevo proceso es creado por cada petición HTTP recibi- da y eliminado cuando la petición es resuelta. La eficiencia es pobre. Esta es la razón por la que han 22 MIDDLEWARE Pensando en Python (III): 3 en raya en la web Pensando en Python (III): 3 en raya en la web En esta tercera entrega de la serie sobre Python aprenderemos a realizar aplicaciones web que acceden a bases de datos. Ilustraremos cómo Python representa una seria opción para el desarrollo de aplicaciones web mediante la extensión de la aplicación de tres en raya desarrollada en las dos entregas anteriores. DIEGO LZ. DE IPIÑA GZ. DE ARTAZA (profesor del departa- mento de Ingeniería del Software de la facultad de Ingeniería (ESIDE) de la Universidad de Deusto) Crearemos una aplicación web que accederá a una base de datos

Upload: tranquynh

Post on 25-Sep-2018

232 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Pensando en Python (III): 3 en raya en la webpaginaspersonales.deusto.es/dipina/publications/PensandoEnPython... · relacional y organizador de tareas. La arquitectura es muy modular,

SOLO PROGRAMADORES nº 120

Introducción

Dada la popularidad de Internet, el desarrollo de apli-caciones con interfaz web, agnósticas de la platafor-ma donde se ejecute el cliente web (navegador), haexperimentado un crecimiento exponencial. A pesarde la menor sofisticación de las interfaces web res-pecto a las interfaces gráficas de aplicaciones desobremesa, su facilidad tanto de uso, dada la familia-ridad de los usuarios con el paradigma web, como susimplicidad de desarrollo, son razones suficientespara considerarlas como primera opción en cualquierdesarrollo que una empresa acomete hoy en día. En las aplicaciones web, una sola instancia de lamisma ejecutándose en un servidor web (o varias enun cluster de servidores), es compartida por varios (enalgunos casos cientos o miles) de usuarios que acce-den a ella desde un cliente universal, el navegador deInternet. El principal cometido de una aplicación webes crear páginas HTML (o en cualquier otro lenguajede marcado) de manera dinámica. Es decir, los datosestáticos predefinidos de una página son combinadoscon datos generados por la lógica de la aplicación uobtenidos de fuentes de datos en tiempo real, porejemplo de una base de datos. Durante la ejecuciónconcurrente de las tareas requeridas simultáneamen-te por los diferentes usuarios de la aplicación web esfrecuente que se detecten conflictos de acceso ymodificación a datos en el servidor. Además, la aplica-ción web debe generar las páginas web, ejecutandotoda la lógica de negocio subyacente, de la manera

más rápida y eficiente posible. Es por tanto necesario,adoptar mecanismos en la parte servidora que agili-cen los accesos a datos y soporten transacciones, esdecir, conjuntos de operaciones físicas relacionadassobre datos, que aparecen al cliente como una unidadlógica. Entran en juego aquí los llamados sistemasgestores de bases de datos (SGBD). La versión máscomúnmente utilizada de los mismos son los sistemasrelaciones de bases de datos. En definitiva, servidoresweb y bases de datos relacionales conforman un bino-mio esencial en la creación de toda aplicación web.A continuación, explicaremos cómo se puedencrear aplicaciones web en Python que acceden abases de datos relacionales. Como resultado deeste proceso transformaremos la aplicación desobremesa tres en raya desarrollada durante lasdos anteriores entregas en una aplicación web.

Programación web en Python

Al tratarse Python de un lenguaje de código abiertoexisten multitud de módulos/librerías para realizarcualquier tarea programática que imaginemos. Enocasiones, como es el caso de la programación web,existen varios módulos diferentes que de una mane-ra más básica o sofisticada nos permiten llevar a cabola misma tarea. Para comprobarlo no tenemos másque visitar el portal de Python, en su sección deTemas/web (http://www.python.org/topics/web/). Allípodemos encontrar un largo listado de estos módu-los, detallando sus diferentes funciones. A continua-ción enumeramos los más destacados, clasificadossegún la categoría de plataforma de desarrollo deaplicación web a la que pertenecen:� Programación CGI Básica:� Módulo CGI de la librería Standard de

Python. CGI (Common Gateway Inteface) esun mecanismo estándar para la ejecución decódigo ejecutable por un servidor web y laobtención de los resultados de tal ejecución.

� Módulo Cookie para la creación y procesa-miento de cookies en aplicaciones web. Unacookie es un mecanismo para mantener esta-do entre las peticiones HTTP de una sesiónweb. Una cookie es una cabecera HTTP quepermite la identificación unívoca en el servi-dor web del peticionario de la información.

� Programación CGI Avanzada. En CGI, un nuevoproceso es creado por cada petición HTTP recibi-da y eliminado cuando la petición es resuelta. Laeficiencia es pobre. Esta es la razón por la que han

22

MIDDLEWARE

Pensando en Python (III):3 en raya en la webPensando en Python (III): 3 en raya en la web

En esta tercera entrega de la seriesobre Python aprenderemos a realizaraplicaciones web que acceden abases de datos. Ilustraremos cómoPython representa una seria opciónpara el desarrollo de aplicacionesweb mediante la extensión de laaplicación de tres en rayadesarrollada en las dos entregasanteriores.

DIEGO LZ. DE IPIÑA GZ. DE ARTAZA (profesor del departa-mento de Ingeniería del Software de la facultad deIngeniería (ESIDE) de la Universidad de Deusto)

Crearemos unaaplicación webque accederá a

una base de datos

python 120.qxd 19/11/04 01:03 Página 22

Page 2: Pensando en Python (III): 3 en raya en la webpaginaspersonales.deusto.es/dipina/publications/PensandoEnPython... · relacional y organizador de tareas. La arquitectura es muy modular,

SOLO PROGRAMADORES nº 120

aparecido numerosas tecnologías que per-miten una integración superior con el servi-dor web subyacente, y lo más importante,una mayor eficiencia. Ejemplos claros deestas tecnologías son PHP, Java Servlets yJSPs, y ASPs. En el caso particular dePython, la contribución más interesante aeste respecto es mod_python:� mod_python es un módulo Apache que

integra el intérprete Python dentro delservidor, de modo que las aplicacionespueden ejecutarse de manera más rápidaque CGI, retiene datos persistentes entrelas peticiones HTTP de una sesión y per-mite acceder a la parte interna de Apache.

� Servidores de aplicaciones. Van más alláde la simple generación de páginas dinámi-cas y asisten al programador en otras tareasde la programación de la lógica de negocio,tales como la persistencia de datos, la ges-tión de transacciones o la seguridad:� Webware facilita una suite de compo-

nentes Python para el desarrollo de apli-caciones web. Provee un servidor de apli-caciones, servlets, Python Server Pages(PSP), mapeo de objetos a bases de datosrelacional y organizador de tareas. Laarquitectura es muy modular, y permiteañadir tus propias extensiones.

� Zope, el Z Object Publishing Environment,proporciona una ORB (Object RequestBroker) HTTP que permite publicar e invo-car objetos Python en la web sin necesi-dad de ningún tipo de código CGI o HTTPespecífico. Objetos complicados puedenser publicados con URLs que simulanjerarquías de objetos. Zope proporcionavarios componentes que pueden usarseen concierto o separadamente. Algunoscomponentes incluidos son: un paquetede plantillas HTML, un sistema de persis-tencia de objetos, una plataforma para lagestión de objetos vía web, e incluso unservidor web sencillo.

En este artículo nos concentraremos en elmódulo mod_python, que a juicio del autorrepresenta la manera más sencilla y eficiente deimplementar aplicaciones web en Python. Losservidores de aplicaciones como Zope repre-sentan una clara alternativa, y aunque tambiéntienen un rendimiento tan elevado o superior amod_python, su uso no es trivial y requeriríansu exposición mucho más detallada.

Integración Apache/Python:mod_python

Grisha Trubetskoy (http://www.ispol.com/home/grisha/) creó mod_python en 2003 con elpropósito de ofrecer un módulo de extensión parael servidor web Apache (www.apache.org) que

permitiese la generación de páginas dinámicascon Python de una manera más eficiente que eltradicional módulo CGI. mod_python empotra elintérprete de Python dentro del servidor Apache.Con mod_python puedes escribir aplicacionesweb en Python que ejecutan muchas veces másrápido que los CGIs tradicionales, tienen acceso acaracterísticas avanzadas tales como retenerconexiones a bases de datos entre conexionesHTTP y permiten acceder a la parte interna deApache. Con mod_python, el código Python seejecuta directamente dentro del servidor Apache,sin necesidad de que éste tenga que lanzar proce-sos externos o delegar las peticiones a servidoresde aplicaciones externos. mod_python se benefi-cia de las habilidades de Apache para aceptar yprocesar peticiones entrantes de una maneraescalable a la carga. El resultado es una platafor-ma que, según Trubetskoy, puede procesar peticio-nes más rápido que cualquier otra plataforma dedesarrollo web en Python. mod_python es:� Un módulo Apache recargable que empotra

el intérprete de Python (libpython) prove-yendo la habilidad de ejecutar códigoPython en el mismo proceso que Apache.

� Un manejador de las fases de procesamien-to de las peticiones HTTP en Apache, que per-mite implementar cualquier fase en Python.

También permite implementar filtros ymanejadores de conexiones en Python. Unfiltro HTTP intercepta de manera dinámicapeticiones y respuestas para transformar ousar la información contenida en las mismas.

� Una interfaz a un subconjunto de las APIsde Apache, permitiendo la invocación defunciones internas del servidor desdePython. Esto permite acceder a informa-ción interna del servidor o de beneficiarsede facilidades del mismo como logeo.

� Una colección de herramientas para eldesarrollo de aplicaciones web. Provee unconjunto de manejadores de peticiones:Publisher (mapea URLs a objetos y funcio-nes dentro de módulos Python), PSP (per-mite la creación de Python Server Pages) yCGI (permite la programación web confor-mando con el estándar CGI). Cada uno deestos manejadores ofrece una maneradiferente de desarrollar aplicaciones web,así como un conjunto de objetos y funcio-nes de utilidad para el procesamiento decookies, gestión de sesiones, y otros aspec-tos comunes en el desarrollo web.

El mayor incoveniente de mod_python es ser espe-cífico a Apache, a diferencia de JSPs y PHP quepueden integrarse con diferentes servidores web.

23

MIDDLEWAREPensando en Python (III): 3 en raya en la web

LISTADO 2 Código del fichero holasolop.psp

<html><%if form.has_key(‘nombre’): # el objeto form es descrito en la siguiente sección

saludo = ‘¡Hola, %s!’ % form[‘nombre’].capitalize()else:

saludo = ‘Hola solop!’# end%>

<h1><%= saludo %></h1></html>

LISTADO 1 Hay que incluir este código XML en el fichero httpd.conf

<Directory “<directorio-instalación-apache>/cgi-bin/python”>SetHandler mod_pythonPythonHandler mod_python.publisherPythonDebug On

</Directory>

<Directory “<directorio-instalación-apache>/cgi-bin/psp”>AddHandler mod_python .pspPythonHandler mod_python.pspPythonDebug On

</Directory>

LISTADO 3 Página de partida (index.html) de la aplicación webTres en Raya

<html><head>

<meta name=”description” content=””><meta name=”keywords” content=””><title>Juego Tres en raya para Solop</title>

</head><frameset cols=”100%” border=”0”>

<frame src=”/cgi-bin/psp/tresenraya.psp” name=”tresenraya”></frameset>

</html>

python 120.qxd 19/11/04 01:04 Página 23

Page 3: Pensando en Python (III): 3 en raya en la webpaginaspersonales.deusto.es/dipina/publications/PensandoEnPython... · relacional y organizador de tareas. La arquitectura es muy modular,

SOLO PROGRAMADORES nº 120

Instalando Apache y mod_python Para poder instalar mod_python es requisitoimprescindible la previa instalación del

intérprete de Python y el servidor webApache. Detalles sobre cómo instalar Pythontanto en Windows como UNIX fueron

cubiertos en la primera entrega deesta serie. Apache ha sido el servidor más popularen Internet desde 1996. En Octubre del2003, 64% de los servidores web enInternet usaban Apache. Para obtener laúltima versión de Apache es necesarioacceder a su página web: http://httpd.apache.org/. La mayoría de lasdistribuciones Linux ya traen preinstala-do este servidor bien en su versión 1.3 o2.0 (la última y la que utilizaremos eneste artículo). Alternativamente, serecomienda que utilices tu gestor depaquetes Linux favorito para conseguiruna copia o bájate el código fuente dehttp://httpd.apache.org/download.cgi ysigue las instrucciones de compilaciónincluidas. Si eres un usuario deWindows bájate el fichero de instala-ción apache_2.xxx-win32-x86-xxx.msiy haz doble clic sobre él (el CD-ROMadjunto incluye la versión de Apachepara Windows).mod_python puede obtenerse dehttp://httpd.apache.org/modules/python-download.cgi. Los usuarios deWindows deben ejecutar el fichero.exe suministrado que instalarámod_python en su sistema. Los usua-rios de UNIX pueden bajarse el códigofuente y las instrucciones de compila-ción del mismo lugar. En la prepara-ción de este artículo hemos usado laversión 3.0 de mod_python (el CD-ROM adjunto incluye la versión demod_python para Windows).

Configurando Apache ymod_python Una vez instalado Apache ymod_python tan sólo resta modificarel principal fichero de configuraciónde Apache, ubicado en <directorio-instalación-apache>/conf/httpd.conf:1.- Al final del bloque de sentenciasLoadModule añadir la siguiente:LoadModule python_module modules/

mod_python.so

2.- Después del bloque XML iniciadopor el elemento Directory y corres-pondiente al directorio htdocs, direc-torio raíz del que cuelgan, por defec-to, los documentos HTML estáticos enApache, colocar los bloques Directorydel listado 1, reemplazando <directo-rio-instalación-apache> por la ruta

de instalación de Apache en tu máquina. Con las definiciones mostradas en el lista-do 1 indicamos que debajo del subdirecto-

24

MIDDLEWARE

LISTADO 5 Código principal de web /cgi-bin/psp/tresenrayaweb.py

import tresenraya

NO_GANADOR = -1EMPATE = 0GANA_JUGADOR = 1PIERDE_JUGADOR = 2

class JuegoTresEnRayaWeb(tresenraya.JuegoTresEnRaya):# falta el constructor y algunos métodos, mirar código completo en CD...def select_casilla(self, x, y):

if not self.jugar(x, y):return self.ganador

else:return NO_GANADOR

def jugar(self, x, y):if self._JuegoTresEnRaya__casillasMarcadas < 9:

if self._JuegoTresEnRaya__casillero[x][y] == None:self._JuegoTresEnRaya__casillero[x][y]=self._JuegoTresEnRaya__marcaJugadorself._JuegoTresEnRaya__casillasMarcadas += 1self.__marcarCasilla(x, y, ‘o.png’)if self._JuegoTresEnRaya__hayGanador():

self._JuegoTresEnRaya__registro.registrarVictoria( self._JuegoTresEnRaya__nombreUsuario)

self.ganador = GANA_JUGADORreturn False

elif self._JuegoTresEnRaya__casillasMarcadas == 9:self._JuegoTresEnRaya__registro.registrarEmpate(

self._JuegoTresEnRaya__nombreUsuario)self.ganador = EMPATEreturn False

casillaIndex = self._JuegoTresEnRaya__elegirMarcaMaquina()self._JuegoTresEnRaya__casillero[casillaIndex[0]][casillaIndex[1]] =

self._JuegoTresEnRaya__marcaMaquinaself._JuegoTresEnRaya__casillasMarcadas += 1self.__marcarCasilla(casillaIndex[0], casillaIndex[1], ‘x.png’)if self._JuegoTresEnRaya__hayGanador():

self._JuegoTresEnRaya__registro.registrarPerdida( self._JuegoTresEnRaya__nombreUsuario)

self.ganador = PIERDE_JUGADORreturn False

elif self._JuegoTresEnRaya__casillasMarcadas == 9:self._JuegoTresEnRaya__registro.registrarEmpate( self._JuegoTresEnRaya__nombreUsuario)

self.ganador = EMPATEreturn False

return True

LISTADO 4 Lógica de control de /cgi-bin/psp/tresenraya.psp

<%import tresenrayaweb# hay que hacer login antes de poder jugar, verificamos si se ha pasado un nombreUsuarioif not form.has_key(‘nombreUsuario’):

# solicitamos nombreUsuario en login.psppsp.redirect(‘/cgi-bin/psp/login.psp’)

else:saludo = ‘¡Bienvenido %s a Tres en raya!’ % form[‘nombreUsuario’].capitalize()

# end

# se crea un objeto partida y se cachea en sesión if not session.has_key(‘partida’):

session[‘partida’] = tresenrayaweb.JuegoTresEnRayaWeb()# de momento no hay ganador ganador = tresenrayaweb.NO_GANADOR # si se pasan como parámetros las coordenadas x e y de una casilla, ésta se seleccionaif form.has_key(‘x’) and form.has_key(‘y’):

ganador = session[‘partida’].select_casilla(eval(form[‘x’]), eval(form[‘y’]))# continua en listado 5 ...

python 120.qxd 19/11/04 01:04 Página 24

Page 4: Pensando en Python (III): 3 en raya en la webpaginaspersonales.deusto.es/dipina/publications/PensandoEnPython... · relacional y organizador de tareas. La arquitectura es muy modular,

SOLO PROGRAMADORES nº 120

rio python de cgi-bin, directoriopor defecto del que cuelgan losscripts CGI en Apache, colocare-mos programas Python que gene-rarán páginas HTML siguiendo elmodelo del manejador de peticio-nes Publisher. Con la segundadirectiva Directory indicamos quebajo el directorio psp colocaremosscripts en formato Python ServerPages (PSP).

Python Server Pages enmod_pythonPython Server Pages (PSP) es unmecanismo para incluir sentenciasPython en documentos HTML o XML.El servidor interpreta el código Pythonincluido para producir el documentoHTML o XML enviado al cliente. Estemecanismo se ha hecho popular através de otras herramientas bienconocidas como JSP, PHP o ASP.Es importante remarcar que mezclarcódigo fuente Python en mitad de undocumento HTML es objeto de ciertacontroversia. Algunos ingenieros delsoftware consideran incluir código enmitad de un documento HTML unamala práctica de programación, al vio-lar el paradigma Modelo-Vista-Controlador, introduciendo lógica denegocio en la capa de presentación. A pesar deno ser tan buena práctica, los millones de pro-gramadores PHP que exitosamente utilizan esteparadigma demuestran que existe grandemanda por este estilo de programación web.No entraremos en discusiones dogmáticas eneste artículo, esto es un artículo sobre Python yno sobre patrones de diseño.La sintaxis de PSP es similar a la original deJSP, delimitando el código Python por mediode los símbolos <% y %>. De igual modo aJSP, PSP tiene cuatro tipos de entidades:� Código. Representa el código fuente

Python que contiene la lógica de cómo lasalida final es producida. Está delimitadopor los códigos de escape <% y %>.

� Expresiones. Es código fuente Pythoncuyo resultado en forma de un string

forma parte de la salida final. Está delimi-tado por los códigos de escape <%= y %>.

� Directivas. Son instrucciones especialespara el procesador PSP. Son delimitadaspor los códigos <%@ y %>. Actualmentemod_python sólo soporta la directiva<%@ include file=’nombre-fichero’>,que reemplazará esta directiva por elcontenido del fichero ‘nombre-fichero’.

� Comentarios. En PSP, son eliminados porel procesador PSP, y son delimitados porlos símbolos <%-- y --%>.

La parte más complicada de la programaciónPSP es la gestión de la tabulación sintáctica dePython. El intérprete de PSP recuerda la últimatabulación Python usada a lo largo del códigoHTML, y deberemos ajustar nuestro códigoPython a la misma. Para simplificar esta tarea

es recomendable usar comentarios que haganel código más legible. Por ejemplo:<%

if x == y:

# comienzo

%>

25

MIDDLEWAREPensando en Python (III): 3 en raya en la web

Figura 1. Nuestra primera aplicación web en PSP.Figura 2. Pantalla web generada por elscript tresenraya.psp.

LISTADO 6 Código de /cgi-bin/psp/login.psp

<%import tresenrayawebimport RegistroJugadoresDB

if not session.has_key(‘registro’):session[‘registro’] = RegistroJugadoresDB.RegistroJugadoresDB()

mensajeError = “”if form.has_key(‘nombreUsuario’) and form.has_key(‘clave’):

try:session[‘registro’].login(form[‘nombreUsuario’], form[‘clave’])psp.redirect(‘/cgi-bin/psp/tresenraya.psp?nombreUsuario=’ + form[‘nombreUsuario’])

except:mensajeError = ‘Los detalles de login introducidos son incorrectos’

saludo = ‘Introduce tus detalles de logeo para jugar al Tres en raya’# end%>

<html> <h1><%= saludo %></h1><form method=”post” action=”/cgi-bin/psp/login.psp”>

<table><tr><td>Nombre usuario:</td>

<td><input type=”text” name=”nombreUsuario”></td></tr><tr><td>Contraseña:</td><td><input type=”password”

name=”clave”></td></tr><tr><td><input type=”submit” value=”Login”></td>

<td><input type=”reset” name=”Limpiar”></td></tr></table>

</form><%if len(mensajeError):%>

<p><%=mensajeError%></p><%# end if%></html>

python 120.qxd 19/11/04 01:04 Página 25

Page 5: Pensando en Python (III): 3 en raya en la webpaginaspersonales.deusto.es/dipina/publications/PensandoEnPython... · relacional y organizador de tareas. La arquitectura es muy modular,

SOLO PROGRAMADORES nº 120

... código html ...

<%

# fin

%>

Hola Solop en el PSP demod_pythonEn el listado 2 mostramos cómo sepodría realizar una simple página PSPque reproduce el mensaje “Hola Solop”al invocar la URL http://local-host:8080/cgi-bin/psp/holasolop.psp.Si se le añade a la URL el sufijo “?nom-bre=lectores+Solop”, el resultadogenerado sería “Hola, Lectores Solop”.En la figura 1 se visualiza el resultadode realizar ésta última invocación.

Variables globalesExisten varias variables accesibles entiempo de ejecución de una páginaPSP. Estas variables pueden ser utiliza-das directamente, sin habérseles asignado nin-gún valor previamente:� req, el objeto Request de mod_python.

Toda la funcionalidad avanzada demod_python es disponible a través deeste objeto en una página PSP.

� psp, corresponde a una instancia del obje-to PSPInstance, que permite acceder a unaAPI específica de PSP. Ofrece los siguientesmétodos:� set_error_page(filename), permite

especificar el nombre de una páginaPSP a invocar cuando ocurre un erroren Python.

� apply_data(object), invocará el obje-to object, pasándole como argumen-tos los campos del formulario y devol-viendo el resultado. Si estás familiari-zado con JSP funciona comosetProperty. Por ejemplo, dado elsiguiente objeto:class Coche:

def __init__(self, color):

self.color = color

Entonces, una página PSP invocada comoresultado de una ejecución de un formu-lario conteniendo un campo con nombre

color, podría hacer lo siguiente:<%

coche = psp.apply_data(Coche)

%>

Esta sentencia invocaría el cons-tructor del objeto Coche, pasando elvalor del campo del formulariocolor. Como resultado, una instan-cia de Coche sería asignada a lavariable coche.� redirect(location), puede utili-zarse para redireccionar entre pági-nas PSP. Debe invocarse como pri-mera sentencia en una página, no se

puede producir una redirección despuésde haber datos de salida al navegador.

� form, corresponde a los campos de unformulario en formato diccionario (obje-to mod_python.FieldStorage).

� session, si PSP detecta que una páginahace referencia a la variable session, auto-máticamente creará un objeto de tipomod_python.Session, que generará cookiesde sesión y permitirá mantener estadoentre las invocaciones de una petición.

Para más información sobre los objetos defi-nidos por mod_python, consultar su manualdisponible en http://www.modpython.org/live/current/doc-html/.

Añadiendo una interfaz web a laaplicación Tres en Raya

Una vez conocida la teoría básica sobre cómocrear y usar objetos mod_python dentro deun PSP, vamos a ilustrar su uso transforman-

26

MIDDLEWARE

LISTADO 7 Código de /cgi-bin/psp/estadisticas.psp

<%import tresenrayawebimport RegistroJugadoresDB

# Es obligatorio hacer login para usar estadisticas.pspif not form.has_key(‘nombreUsuario’):

psp.redirect(‘/cgi-bin/psp/login.psp’)# end%>

<html><%estadisticasUsuario = session[‘registro’].getEstadisticas(form[‘nombreUsuario’])%><h1>Las estadísticas de partidas jugadas por <%=form[‘nombreUsuario’]%> son:</h1><table border=”1”>

<tr><th>Ganadas</th><th>Empatadas</th><th>Perdidas</th></tr> <tr><td><%=estadisticasUsuario[0]%></td><td><%=estadisticasUsuario[1]%></td>

<td><%=estadisticasUsuario[2]%></td></tr></table>

<hr><a href=”/cgi-bin/psp/tresenraya.psp?nombreUsuario=<%=form[‘nombreUsuario’]%>”>Inicianueva partida</a><br><a href=”/cgi-bin/psp/login.psp”>Login como otro usuario</a> </html>

Figura 3. Pantalla web generada por script login.psp.

LISTADO 8 Código SQL para crear la base de datos tresenraya

CREATE DATABASE tresenraya;GRANT ALTER, SELECT,INSERT,UPDATE,DELETE,CREATE,DROP

ON tresenraya.*TO tresenraya@localhostIDENTIFIED BY ‘tresenraya’;

CREATE TABLE usuario (nombreUsuario varchar(250) not null primary key,password varchar(250)

);CREATE TABLE estadistica(

nombreUsuario varchar(250) not null primary key references usuario(nombreUsuario),

ganadas int(11) default 0,empatadas int(11) default 0,perdidas int(11) default 0

);

python 120.qxd 19/11/04 01:04 Página 26

Page 6: Pensando en Python (III): 3 en raya en la webpaginaspersonales.deusto.es/dipina/publications/PensandoEnPython... · relacional y organizador de tareas. La arquitectura es muy modular,

SOLO PROGRAMADORES nº 120

do nuestra aplicación de tres en raya en unaaplicación web.En Apache los documentos estáticos se guar-dan, por defecto, en su directorio htdocs. Paranuestra aplicación crearemos un subdirectorio“tresenraya” debajo de htdocs, y colocaremosallí un fichero index.html que apunta al fiche-ro .psp que actúa como controlador de la apli-cación tres en raya. Además, crearemos unsubdirectorio “images” donde guardaremos losficheros .png que indican que una celda noestá marcada (blank.png), está marcada por eljugador (o.png) o la máquina (x.png). El listado3 muestra el fichero index.html que incrustaun marco (frame) que contiene otro marco deigual tamaño. El marco interior irá cambiandode contenido durante la ejecución de la aplica-ción, mientras que el marco exterior permane-cerá haciendo que la URL que aparece en elnavegador no cambie. Es un buen truco paraevitar que el usuario de la aplicación web sepaqué PSP se está ejecutando en cada ocasión, e

incluso con qué tecnología se ha desarrolladola aplicación. Por otra parte, debajo del directorio cgi-bin,donde normalmente se colocan ficheros quegeneran documentos XML de manera dinámica,creamos el subdirectorio psp, en el queguardamos los siguientes ficheros:� tresenraya.psp: script PSP que

visualiza el tablero del juego de tresen raya y actúa también comocontrolador del resto de PSPs. Si unusuario intenta acceder a este PSPy no ha hecho login previamenteserá redirigido a login.psp. Por otrolado, si un usuario que ha hechologin desea ver las estadísticas deresultados de las partidas que hajugado, este PSP le redirigirá aestadisticas.psp. El listado 4 mues-tra la lógica de control correspon-diente a este script. Hace que seredireccione a otros PSPs o se invo

que al método select_casilla delobjeto JuegoTresEnRayaWeb, defi-nido en tresenrayaweb.py. En lafigura 2 podemos ver el resultadode haber invocado este PSP hastacompletar una partida de tres enraya.

� tresenrayaweb.py: define la claseJuegoTresEnRayaWeb que heredade la clase JuegoTresEnRaya, imple-mentada en las anteriores entregas.Simplemente sobrescribe su méto-do jugar() y declara un nuevo méto-do público select_casilla. En tresen-raya.psp se añade a la variablesesión una instancia de esta clase.El listado 5 muestra las partes másimportantes de la implementaciónde esta clase.

� login.psp: script PSP que comprue-ba en primer lugar si existe una ins-tancia del objeto de tipoRegistroJugadores (o una de sussubclases), implementada en lasanteriores entregas, en la variableglobal session. Si no es así la crea. Sien la invocación se han pasado losparámetros nombreUsuario y clave,correspondientes a un usuarioregistrado, se redirecciona a tresen-raya.psp. El listado 6 muestra elcontenido de este PSP. Es impor-tante resaltar que login.psp creauna instancia de la claseRegistroJugadoresDB que deriva deRegistroJugadores, aunque en estaocasión guarda los datos en unabase de datos. En la siguiente sec-ción se describirá esta clase en másdetalle. En la figura 3 se puede verel resultado de invocar el scriptlogin.psp.

� estadisticas.psp: script PSP que visualiza enuna tabla HTML las partidas ganadas, empa-

27

MIDDLEWAREPensando en Python (III): 3 en raya en la web

Figura 4. Pantalla web generada por el scriptestadisticas.psp.

LISTADO 9 Clase RegistroJugadoresDB

# importar módulos específicos de MySQL: MySQLdbimport MySQLdb, string, _mysql, _mysql_exceptions, tresenrayaclass RegistroJugadoresDB(tresenraya.RegistroJugadores):

def __init__(self):tresenraya.RegistroJugadores.__init__(self)db=MySQLdb.connect(host=”localhost”, user=”tresenraya”,

passwd=”tresenraya”, db=”tresenraya”)self.cursor = db.cursor()# Asegurarse que si no existe un usuario solop se añadausuarioSolop = self._executeSQLCommand(

“select * from usuario where nombreUsuario=’solop’”)# lógica para añadir usuario ‘solop’ si no existe

def guardarEnDB(self):# Guardar por cada usuario sus detalles de login y sus estadísticas for usuario in self._RegistroJugadores__jugadores.keys():

try:self._executeSQLCommand(“insert into usuario values(‘“ + usuario +

“‘, ‘“ + self._RegistroJugadores__jugadores[usuario] + “‘)”)except:

self._executeSQLCommand(“update usuario set password=’” +self._RegistroJugadores__jugadores[usuario] + “‘ where nombreUsuario=’” + usuario + “‘“)

...

def _executeSQLCommand(self, command):# si la consulta devuelve resultados lo hará como una lista de tuplas (filas)# cada elemento de la tupla será una columnaresultado = []command = string.strip(command)if len(command):

try:resultCode = self.cursor.execute(command) # Ejecuta el comandoif string.lower(command).startswith(‘select’): # si es una select ...

filas = self.cursor.fetchall() # recuperar todos los resultadosfor fila in filas:

contenidoFila = []for columna in fila:

if columna == None:contenidoFila.append(None)

else:contenidoFila.append(columna)

resultado.append(tuple(contenidoFila))except _mysql_exceptions.ProgrammingError, e:

print esys.exit()

return resultado

python 120.qxd 19/11/04 01:04 Página 27

Page 7: Pensando en Python (III): 3 en raya en la webpaginaspersonales.deusto.es/dipina/publications/PensandoEnPython... · relacional y organizador de tareas. La arquitectura es muy modular,

SOLO PROGRAMADORES nº 120

tadas y perdidas por el jugador que ha hechologin. El listado 7 muestra el código fuentede este PSP. En la figura 4 se puede ver elresultado de invocar estadisticas.psp. En vezde producir una simple tabla HTML podría-mos haber generado una imagen en forma-to PNG conteniendo un gráfico de barrascomo hicimos en la versión del programa enwxPython. Para ello podríamos haber utiliza-do por ejemplo el modulo matplotlib, dispo-nible en http://matplotlib. sourceforge.net

Programación de bases dedatos en Python

Al igual que Java ofrece el estándar JDBC para laprogramación de bases de datos, Python hace lopropio con su DB-API. Con esta API Python con-sigue aislar el código fuente de la base de datossubyacente. El código Python se acomodará alos interfaces de la DB-API, mientras que unaimplementación de esta API para cada sistemade bases de datos, traducirá invocaciones deDB-API en llamadas de la base de datos. En lasección del portal Python, temas/basesdedatos(http://www.python.org/topics/database/) sepuede encontrar información detallada sobreesta API.

DB-APIA continuación enumeramos los principalesinterfaces ofrecidos por la DB-API:� Para conectarnos a una base de datos

usamos el método connect del módulo de

base de datos utilizado que devuelve unobjeto de tipo conection.

� El objeto conection ofrece el método cur-sor() que sirve para recuperar un cursor de laBD. Otros métodos definidos en connectionson close(), commit(), rollback() y cursor().

� El objeto cursor define entre otros lossiguientes métodos:� execute() nos permite enviar una sen-

tencia SQL a la BD.� fetchone() recupera una fila.� fetchall() recuperar todas las filas.

Como se ha comentado, cada sistema relacio-nal de bases de datos ha de ofrecer una imple-mentación de DB-API. Dada la naturaleza decódigo abierto de Python, hay varios módulosque implementan este estándar:� DCOracle (http://www.zope.org/Products/

DCOracle/) creado por Zope para Oracle� MySQLdb (http://sourceforge.net/projects/

mysql-python) para MySQL.� PyDB2 (http://sourceforge.net/projects/

pydb2) para DB2.� Y otros muchos más…Nosotros utilizaremos la implementación deDB-API para MySQL, una de las bases de datosde código abierto más populares en Internet.

MySQL y MySQLdbMySQL fue creada por la empresa sueca MySQLAB (www.mysql.com). MySQL es una parte esen-cial del proyecto LAMP (Linux, Apache, MySQL,PHP/Perl/Python), una pila de herramientas desoftware abierto para desarrollar aplicaciones de

empresa que está experimentando un gran creci-miento. Cada vez más compañías utilizan LAMPcomo una alternativa a software propietario. La instalación de MySQL es muy sencilla tantoen Linux como Windows. En la siguiente URL sepueden obtener RPMs y ejecutables para insta-lar la última versión de MySQL (4.0) tanto enLinux como Windows: http://dev.mysql.com/downloads/mysql/4.0.html. Por su parte, la instalación de MySQLdb,implementación para MySQL de la DB-API, estambién trivial. Simplemente accede a supágina web (http://sourceforge.net/projects/mysql-python) y usa el instalador provistopara Windows o el RPM para Linux.

Usando MySQL para la aplicaciónTres en Raya

Cómo último paso en nuestro proceso de mejorade la aplicación Tres en Raya, vamos a guardar losdetalles de los jugadores autorizados y las esta-dísticas sobre los resultados de sus partidas enuna base de datos. El listado 8 ilustra cómo crearen MySQL una base de datos de nombre tresen-raya, a la que podemos hacer login de maneralocal con el usuario tresenraya y clave tresenraya,y que define las tablas usuario y estadistica. Una vez creada la base de datos, creamos unanueva clase en Python a la que llamamos,RegistroJugadoresDB, que hereda de la claseRegistroJugadores que hemos ido utilizando yextendiendo desde el primer artículo. El listado 9muestra un fragmento de la implementación deesta clase, dentro del módulo RegistroJugadoresDB.Obsérvese que el módulo MySQLdb es lo primeroque se importa en este módulo. El métodoexecuteSQLCommand invoca toda operación SQLsobre la base de datos. Para más detalles, consultarel código incluido en el CD-ROM.Finalmente sólo nos resta indicar cómo inte-grar el uso de esta clase dentro de nuestrocódigo PSP. El listado 10 muestra cómo al fina-lizar una partida se procederá a guardar losdetalles de los usuarios y los resultados de laspartidas que han jugado, invocando el métodode RegistroJugadoresDB, guardarEnDB().

Conclusiones

En esta tercera entrega de la serie sobrePython hemos aprendido cómo desarrollaraplicaciones web que acceden a bases dedatos a través de los módulos mod_python yMySQLdb, respectivamente. En la siguienteentrega concluiremos nuestro estudio dePython, ilustrando el potencial del mismo en elprocesamiento de XML y mirando a dos pri-mos hermanos de Python, JPython, la imple-mentación Java de Python e IronPython, laimplementación .NET de Python.

28

MIDDLEWARE

LISTADO 10 Guardando estadísticas en /cgi-bin/psp/tresenraya.psp

<%if ganador == tresenrayaweb.EMPATE:

session[‘partida’].limpiar_tablero()session[‘registro’].registrarEmpate(form[‘nombreUsuario’])session[‘registro’].guardarEnDB()

%><p>¡Ha habido empate!</p>

<%elif ganador == tresenrayaweb.GANA_JUGADOR:

session[‘partida’].limpiar_tablero()session[‘registro’].registrarVictoria(form[‘nombreUsuario’])session[‘registro’].guardarEnDB()

%><p>¡El jugador ha ganado!</p>

<%elif ganador == tresenrayaweb.PIERDE_JUGADOR:

session[‘partida’].limpiar_tablero()session[‘registro’].registrarPerdida(form[‘nombreUsuario’])session[‘registro’].guardarEnDB()

%><p>¡La máquina ha ganado!</p>

<%# end if%><hr><a href=”/cgi-bin/psp/tresenraya.psp?nombreUsuario=<%=form[‘nombreUsuario’]%>

”>Inicia nueva partida</a><br><a href=”/cgi-bin/psp/estadisticas.psp?nombreUsuario=<%=form[‘nombreUsuario’]%>

”>Ver estadísticas jugador</a><br><a href=”/cgi-bin/psp/login.psp”>Login como otro usuario</a></html>

python 120.qxd 19/11/04 01:04 Página 28

Page 8: Pensando en Python (III): 3 en raya en la webpaginaspersonales.deusto.es/dipina/publications/PensandoEnPython... · relacional y organizador de tareas. La arquitectura es muy modular,

Anuncio ML en Solop 120.qxd 19/11/04 01:16 Página 1