El libro de Django 1.0

20.3. Usando Django con Apache y mod_python

Apache con mod_python es actualmente la configuración más robusta para usar Django en un servidor en producción.

mod_python es un plugin de Apache que embebe Python dentro de Apache y carga código Python en memoria cuando el servidor se inicia. El código permanece en memoria a lo largo de la vida del proceso Apache, lo que repercute en aumentos significativos de desempeño comparado con otros arreglos de servidor.

Django requiere Apache 2.x y mod_python 3.x, y nosotros preferimos el módulo de multiprocesamiento (MPM) prefork de Apache, por sobre el MPM worker.

Nota Configurar Apache está claramente más allá del alcance de este libro, por lo que simplemente mencionaremos algunos detalles que necesitamos. Afortunadamente existen grandes recursos disponibles para aprender más sobre Apache. Algunos de los que nos gustan son los siguientes:

20.3.1. Configuración básica

Para configurar Django con mod_python, primero debe asegurarse de que tiene Apache instalado con el módulo mod_python activado. Esto normalmente significa tener una directiva LoadModule en tu archivo de configuración de Apache. Puede parecerse a esto:

LoadModule python_module /usr/lib/apache2/modules/mod_python.so

Luego, edite su archivo de configuración de Apache y agregue lo siguiente:

<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE misitio.settings
    PythonDebug On
</Location>

Asegurese de reemplazar misitio.settings por el DJANGO_SETTINGS_MODULE apropiado para tu sitio.

Esto le dice a Apache, "Usa mod_python para cualquier URL en '/' o bajo ella, usando el manejado mod_python de Django". Le pasa el valor de DJANGO_SETTINGS_MODULE de modo que mod_python conoce que configuración utilizar.

Nota que estamos usando la directiva <Location> y no <Directory>. Esta última se utiliza para apuntar a lugares de nuestra sistema de archivos, mientras que <Location> apunta a lugares en la estructura de la URL de un sitio web. <Directory> no tendría sentido aquí.

Apache normalmente se ejecuta como un usuario diferente de tu usuario normal y puede tener una ruta y un sys.path distintos. Puedes necesitar decirle a mod_python cómo encontrar tu proyecto y a Django mismo:

PythonPath "['/ruta/al/proyecto', '/ruta/a/django'] + sys.path"

También puedes agregar directivas como PythonAutoReload Off para ajustar el rendimiento. Mira la documentación de mod_python para un obtener un listado completo de opciones.

Ten en cuenta que deberías configurar PythonDebug Off en un servidor de producción. Si dejas PythonDebug On, tus usuarios verán mensajes de error no muy bonitos si algo sale mal dentro de mod_python.

Reinicia Apache, y cualquier petición a tu sitio (o a tu host virtual si pusiste las directivas dentro de un bloque <VirtualHost>) será servida por Django.

Nota Si implementas Django en un subdirectorio — esto es, en algún lugar más profundo que "/" — Django no recortará el prefijo de la URL para tu URLpatterns. Entonces, si tu configuración de Apache luce como esto:

<Location "/misitio/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE misitio.settings
    PythonDebug On
</Location>

entonces todos tus patrones de URL deberán comenzar con "/misitio/". Por esta razón es que normalmente recomendamos implementar Django sobre la raíz de tu dominio o host virtual. Alternativamente, simplemente puede hacer descender el nivel de tu URL usando una cuña de URLconf:

urlpatterns = patterns('',
    (r'^misitio/', include('normal.root.urls')),
)

20.3.2. Corriendo multiples instalaciones de Django en la misma instancia Apache

Es enteramente posible correr multiples instalaciones de Django en la misma instancia de Apache. Probablemente quieras hacer esto si eres un desarrollador web independiente con multiples clientes pero un sólo un único servidor.

Para lograr esto, simplemente usa VirtualHost así:

NameVirtualHost *

<VirtualHost *>
    ServerName www.ejemplo.com
    # ...
    SetEnv DJANGO_SETTINGS_MODULE misitio.settings
</VirtualHost>

<VirtualHost *>
    ServerName www2.ejemplo.com
    # ...
    SetEnv DJANGO_SETTINGS_MODULE misitio.other_settings
</VirtualHost>

Si necesitar poner dos instalaciones de Django sobre el mismo VirtualHost, necesitar prestar especial atención para asegurarte de que el caché de código de mod_python no mezcle las cosas. Usa la directiva PythonInterpreter para brindar diferentes directivas <Location> a interpretes distintos:

<VirtualHost *>
    ServerName www.ejemplo.com
    # ...
    <Location "/algo">
        SetEnv DJANGO_SETTINGS_MODULE misitio.settings
        PythonInterpreter misitio
    </Location>

    <Location "/otracosa">
        SetEnv DJANGO_SETTINGS_MODULE misitio.other_settings
        PythonInterpreter misitio_otro
    </Location>
</VirtualHost>

Los valores de PythonInterpreter no importante realmente ya que se encuentran en dos bloques Location diferentes.

20.3.3. Corriendo un servidor de desarrollo con mod_python

Debido a que mod_python cachea el código python cargado, cuando implementas sitios Django sobre mod_python necesitarás reiniciar Apache cada vez que realizar cambios en tu código. Esto puede ser tedioso, por lo que aqui compartimos un pequeño truco para evitarlo: simplemente agrega MaxRequestsPerChild 1 a tu archivo de configuración para forzar a Apache a recargar todo con cada petición. Pero no hagas esto en un servidor de producción, o revocaremos tus privilegios Django.

Si eres el tipo de programador que depuran dispersando sentencias print por el código (nosotros somos), ten en cuenta que print no tiene efectos sobre mod_python; estas no aparecen en el log de Apache como pudrías esperar. Si necesitas imprimir información de depuración en una configuración mod_python, probablemente quieras usar el paquete de registro de eventos estándar de Python (Python's standard logging package). Hay más información disponible en http://docs.python.org/lib/module-logging.html. Alternativamente, puedes agregar la información de depuración a las plantillas de tu página.

20.3.4. Sirviendo Django y archivos multimedia desde la misma instancia Apache

Django no debería ser utilizado para servir archivos multimedia (imagen, audio, video, flash) por sí mismo; mejor deja ese trabajo al servidor web que hayas elegido. Recomendamos usar un servidor Web separado (es decir, uno que no está corriendo a la vez Django) para servir estos archivos. Para más información, mira la sección "Escalabilidad".

Sin embargo, si no tienes opción para servir los archivos multimedia que no sea el mismo VirtualHost Apache que usa Django, aquí te mostramos como desactivar mod_python para una parte particular del sitio:

<Location "/media/">
    SetHandler None
</Location>

Cambia Location a la URL raiz donde se encuentran tus archivos.

Tambien puedes usar <LocationMatch> para comparar con una expresión regular. Por ejemplo, esto configura Django en la raiz del sitio pero deshabilitando Django para el subdirectorio media y cualquier URL que termine en .jpg, .gif, o .png:

<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
</Location>

<Location "/media/">
    SetHandler None
</Location>

<LocationMatch "\.(jpg|gif|png)$">
    SetHandler None
</LocationMatch>

En todos estos casos, necesitarás configurar la directiva DocumentRoot para que Apache sepa dónde encontrar tus archivos estáticos.

20.3.5. Manejo de errores

Cuando usas Apache/mod_python, los errores serán canalizados por Django — en otras palabras, estos no se propagan al nivel de Apache y no aparecerán en el error_log del servidor.

La excepción a esto sucede si algo está realmente desordenado en tu configuración Django. En este caso, verás una página "Internal Server Error" en tu navegador, y el volcado de error (traceback) de Python completo en tu archivo error_log de Apache. Este volcado de error se difunde por multiples líneas. (Sí, es feo y bastante difícil de leer, pero así como mod_python hace las cosas).

20.3.6. Manejando fallas de segmentación

Algunas veces, Apache produce errores de tipo Segmentation faults cuando instalas Django. Cuando esto sucede, se trata casi siempre de una o dos causas no muy relacionadas con Django en sí:

  • Puede ser que tu código Python está importando el módulo pyexpat (usado para parseo XML), lo que puede entrar en conflicto con la versión embebida en Apache. Para información detallada, revisa "Expat Causing Apache Crash" en http://www.djangoproject.com/r/articles/expat-apache-crash/.
  • Puede deberse a que estás corriendo mod_python y mod_php sobre la misma instancia de Apache, con MySQL como motor de base de datos. En algunos casos, esto ocasiona un conocido problema que mod_python tiene debido a conflictos de versión en PHP y el back-end MySQL de la base. Hay información detallada en un listado FAQ de mod_python, accesible via http://www.djangoproject.com/r/articles/php-modpython-faq/

Si continuas teniendo problemas para configurar mod_python, una buena cosa para hacer es poner un esqueleto de sitio sobre mod_python a funcionar, sin el framework Django. Esta es una manera fácil de aislar los problemas específicos de mod_python. El artículo "Getting mod_python Working" detalla el procedimiento: http://www.djangoproject.com/r/articles/getting-modpython-working/.

El siguiente paso debería ser editar tu código de pruebas y agregar la importación de cualquier código específico de Django que estes usando — tus vistas, tus modelos, tu URLconf, la configuración de RSS, y así. Incluye estas importaciones en tu función de gestión de pruebas, y accede a la URL correspondiente desde tu navegador. Si esto causa un colapso, habrás confirmado que es la importación de código Django la causa del problema.

Gradualmente reduce el conjunto de importaciones hasta que el colapso desaparezca, de manera de encontrar el módulo específico que es el culpable. Profundiza en los módulos y revisa sus importaciones si es necesario. Para más ayuda, herramientas de sistema como ldconfig en Linux, otool en Mac OS, y
ListDLLs (de SysInternals) en Windows pueden ayudarte a indentificar dependencias compartidas y posibles conflictos de version.