Frecuentemente, todos los pythoneros nos llenamos la boca diciendo que «sin un framework, crear una WebApp en Python es imposible». Pero claro, que esa frase no es menos exagerada que decir «si pierde mi equipo de fútbol me mato». Son «formas de decir».
La pregunta es ¿qué sucede si no tienes necesidad de crear una gran app? En ese caso ¿no es un desperdicio utilizar un framework como Django, por ejemplo?
Aquí es entonces, cuando surge la idea de este simple y sencillo tuto, para que en solo 3 pasos, tengas tu Web en Python, sobre Apache y por qué no, conviviendo con otras App en PHP.
Paso 1: Instalar mod_wsgi
mod_wsgi
es un módulo de Apache que provee una interfaz WSGI para correr aplicaciones Web en Python sobre Apache. Y esto, es todo lo que necesitas para que tus archivos .py
se ejecuten por medio de un navegador Web. Asumiendo que YA tienes instalado Python y Apache, solo debes descargar el paquete libapache2-mod-wsgi
para ejecutar archivos en Python 2+ o libapache2-mod-wsgi-py3
para Python 3+.
apt-get install libapache2-mod-wsgi
Paso 2: Configurar el virtual host
En el virtual host, está la clave para correr aplicaciones Web en Python sobre Apache.
El DocumentRoot
-al igual que se hace tradicionalmente con cualquier Web Site-, deberá ser un directorio servido pero, aquí NO colocaremos nuestra Python App. Este directorio, solo almacenará archivos estáticos o cualquier otro que no sea .py
El truco, estará entonces, en una variable: WSGIScriptAlias
a la cual le indicaremos que cualquier petición que llegue a nuestra Web, deberá ser procesada por el script WSGI de nuestra Python App (la cuál, deberá almacenarse en un directorio no servido).
Antes de configurar el virtual host, tengamos en claro como será la estructura de directorios:
misitio.com/
├── <strong>application/ (a)</strong>
│ ├── <strong>application.wsgi (b)</strong>
│ └── modulos_python_de_mi_app/
└── <strong>public_html/ (c)</strong>
(a) Directorio application
: directorio NO servido que almacenará toda nuestra app en Python
(b) Archivo application.wsgi
: script wsgi que manejará todas las peticiones del usuario a través de la URI
(c) Directorio public_html
: directorio SERVIDO que solo almacenará archivos estáticos, nada o vaya uno a saber que cosa mientras que no sea nuestra Python App
Con esto en claro, solo quedará crear y configurar el virtual host para nuestra nueva Web:
1) Crear el archivo virtual host
echo "" > /etc/apache2/sites-available/misitio.com
2) Configurar el virtual host
<virtualhost *:80>
ServerName misitio.com
DocumentRoot /ruta/a/public_html
<strong>WSGIScriptAlias / /ruta/a/application/application.wsgi
ErrorLog /ruta/a/logs/py_errors.log
CustomLog /ruta/a/logs/py_access.log combined
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
</VirtualHost>
2.1) Agregar el nuevo host
Si estás trabajando localmente, para no sobreescribir tu virtual host por defecto (archivo default
), creando un nuevo virtual host, tienes la posibilidad de agregarlo al /etc/hosts
para que en vez de correrlo mediante http://localhost
puedas correrlo a través de http://mi_otro_host
, por ejemplo:
127.0.0.1 misitio.com
3) Habilitar el nuevo sitio y el módulo rewrite de Apache
a2enmod rewrite | a2ensite misitio.com
4) Reiniciar Apache
service apache2 restart
Paso 3: Crear el archivo application.wsgi
Este archivo, recordemos que estará alojado en nuestra carpeta no servida, application. En este archivo necesitaremos una función application la cual tomará dos parámetros: environ
del módulo os
, que provee de un diccionario de las peticiones HTTP estándar y la función start_response
, encargada de entregar la respuesta HTTP al usuario. Veamos un ejemplo sencillo:
# -*- coding: utf-8 *-*
import os, sys
sys.path.append('/ruta/a/application')
def application(environ, start_response):
output = "<b>Hola Mundo!</b>"
start_response('200 OK',
[('Content-Type',
'text/html; charset=utf-8')])
return output
Si ingresas a tu sitio Web (el configurado anteriormente), podrás ver la frase «Hola Mundo!» en negritas.
Como puedes ver, la variable temporal output
es quien almacenará todo el contenido HTML (ya con el render hecho). Esto significa, que dicha variable, podría por ejemplo, obtener su valor desde la llamada a un módulo propio de nuestra Python App:
from mipaquete.mimodulo import mi_funcion
output = mi_funcion()
Incluso, podría ser aún más dinámico, switcheando la petición de REQUEST_URI
:
peticion = environ['REQUEST_URI']
if peticion == '/':
from mipaquete.mimodulo import mi_funcion_por_defecto
output = mi_funcion_por_defecto()
elif peticion == '/contacto':
from mipaquete.contacto import mostrar_form_de_contacto
output = mostrar_form_de_contacto()
else:
output = "No se a dónde quieres acceder!!"
Es importante aclarar que con cada cambio que se haga a la aplicación, se debe reiniciar Apache para que los cambios se reflejen sin conflictos.
Otro aspecto a tener en cuenta, es que cuando se produzcan errores 500 (Internal Server Errors) hay que acostumbrarse a mirar los logs en el archivo py_errors.log
que configuramos en el virtual host, para conocer el error producido y así poder resolverlo.
Puedes además, descargar una mini-python-app de muestra, desarrollada con este tuto.
Espero que les haya servido y ¡hasta la próxima!
17 ideas sobre “Una Web en Python sobre Apache, sin frameworks y en solo 3 pasos”
Me ha gustado esta alternativa para tener una web , esta tarde lo probare, y muy bien explicado 😉
Qué bueno Euge -;) Este tipo de entradas son las que se agradece leer, creo me toca la próxima a mi !!
Un abrazo
Excelentemente explicado, muy simple, nunca había pensado en algo web con Python sin un framework como Django, como bien comentas en este post.
Sencillo y rápido, aunque prefiero usar un framework que trabajar así pelón con apache, python y wsgi.
muchas gracias eugeniabahit esta excelente tu explicación
Otra demostración de poderío de Miss Python, qué maravilla 🙂
muchas gracias Euge…muy bien explicado ahora lo probare a ver si no me vuelvo loco jajaj digo cabe mencionar que soy super novato en estos menesteres jeje.
saludos.
Buen tutorial y me permito agregar algo…
Si se configura el mod_wsgi en modo daemon se puede reiniciar en cada peticion, se hace colocando esta rutina en el script .wsgi:
if environ['mod_wsgi.process_group']:
import signal
os.kill(os.getpid(), signal.SIGINT)
Y para que esto funcione se declaran las siguientes variables en el VirtualHost:
WSGIDaemonProcess nombre_cualquiera threads=25
WSGIProcessGroup nombre_cualquiera
Espero sirva de algo este pequeño agregado.
Excelente aporte, Alvaro! Muchísimas gracias por tu colaboración! 🙂
Solo agregar que esta alternativa, es excelente para implementar en servidores de desarrollo y en lo posible, hay que tratar de evitar implementarla en servidores de producción, puesto que en apps con tráfico alto, puede verse afectado el rendimiento operativo del server.
Mil gracias de nuevo!
Un abrazo!
Eugenia
Gracias a ti, para mi es un placer colaborar.
Tienes toda la razón, particularmente uso esta practica en mi servidor local de desarrollo.
Hola. Gracias por el artículo, ahora mismo estoy construyendo una web sin ningún framework.
Pero tengo una duda. ¿Como hacés para establecer conecciones seguras para enviar datos de formularios?.
Saludos y gracias.
Hola Alan! No entiendo a qué te referís con «establecer conexiones seguras para enviar datos de formularios».
Bueno, digamos que tengo este formulario:
Usuario
Contraseña
Y uso tu application.wsgi así:
# -*- coding: utf-8 *-*
import os, sys
sys.path.append(‘/ruta/a/application’)
def application(environ, start_response):
peticion = environ[‘REQUEST_URI’]
if peticion = ‘/validar’:
from mipaquete.validar import validar_formulario
ouput = validar_formulario()
start_response(‘200 OK’,
[(‘Content-Type’,
‘text/html; charset=utf-8’)])
return output
Entonces, la pregunta es ¿como hago que los datos del formulario se envíen encriptados al servidor? O sea, quiero proteger esos datos de alguna manera.
@alan, en ese caso, no depende de Python. Para enviar datos encriptados, podés trabajar a través de HTTPS (necesitarás un certificado de seguridad, pero a lo que voy es que no necesitas de Python para eso). Salvo que, lo que estés buscando, sea hashear esos datos (y no enviarlos encriptados).
Si lo que necesitás es hashearlos, simplemente podés utilizar hashlib. Para levantar los datos desde un form, podés hacerlo através de la clave wsgi.input del diccionario environ. Es un objeto tipo file que lees con read:
datos_form = environ[‘wsgi.input’].read()
Eso te retorna los datos como un querystring:
campo1=valor&campo2=otro_valor
Ah, ok. Voy a investigar eso del HTTPS. Yo hasheaba los datos con la librería cgi y no sabía que se podía hacer como vos decís.
Gracias por tu ayuda 😀
hola eugenia, sabes me e instalado un servidor ISP con un panel ISPConfig, todo trabaja bien, mi problema es que no entiendo mucho sobre los DNS, no se como asignarles IP, no e logrado comunicar un dominio comprado en Gandi.net hacia el servidor.
Como le podría hace, como creo las IP para los DNS?
Hola emmanuel, este post no va sobre ISP Config, lee por favor esto: http://www.howtoforge.com/how-to-run-your-own-name-server-with-ispconfig-3-and-fast-hosts
Saludos