Contenido

Django no encuentra las CSS del módulo Admin

No es la primera vez que pierdo un montón de tiempo para conseguir que Django me sirva los estáticos en producción, aunque sólo sea los del módulo Admin, así que he decidido escribir un post para no olvidarme.

Aunque servir estáticos con Django está desaconsejado por la propia documentación, ya que puede ser inseguro e ineficiente, yo lo encuentro muy útil para servir los estáticos del módulo Admin.

Django

El problema

El principal problema es que Django no sirve los estáticos cuando se tiene DEBUG = False. Claro está, no nos interesa que cualquier problema con la web muestre todas las variables de entorno (incluyendo contraseñas en algunos servidores).

Así que se trata de conseguir servir estáticos cuando DEBUG = True.

Cambios en archivos

Si no indico lo contrario, habrá que modificar el archivo settings.py.

Indicar dónde están los estáticos

Lo primero es indicar dónde están los estáticos. Para ello tendremos que incluir la applicación django.contrib.staticfiles:

1
2
3
4
5
INSTALLED_APPS = (
    # ...
    'django.contrib.staticfiles',
    # ...
)

Si hemos sobreescrito la variable STATICFILES_FINDERS, será importante añadir el finder AppDirectoriesFinder, con el fin de que se sirvan los estáticos de las aplicaciones:

1
2
3
4
5
STATICFILES_FINDERS = [
    # ...
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
    # ...
]

Permisos

Ahora debemos dar permiso de acceso a los estáticos. Bueno… Estáticos y no estáticos, ya que cuando DEBUG vale True podemos acceder a todo, pero cuando vale False no nos dejará acceder a nada:

1
2
3
4
5
6
7
import os
from socket import gethostname

ALLOWED_HOSTS = [
    gethostname(),
    os.environ.get('OPENSHIFT_APP_DNS', 'localhost'),
]

Como veis, aquí permito acceder mediante el nombre de la máquina, o desde el DNS de [OpenShift], si lo estamos usando. Si no estamos en [OpenShift], entonces usa localhost.

Rutas

Por último, hay que modificar las rutas de acceso. Eso está en el archivo urls.py, donde tendremos que incluir a mano los estáticos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from django.conf import settings
from django.conf.urls import url

urlpatterns += [
    url(
        r'^static/(?P<path>.*)$',
        'django.views.static.serve',
        {
            'document_root': settings.STATIC_ROOT,
            'show_indexes': settings.DEBUG,
        }
    ),
]

Conclusión

Y eso es todo. No sé ni cuántas horas he perdido con esta tontería, sobre todo hasta descubrir el párrafo:

During development, if you use django.contrib.staticfiles, this will be done automatically by runserver when DEBUG is set to True (see django.contrib.staticfiles.views.serve()).

Es horrible que se comporte de forma distinta en modo DEBUG del modo producción, y que sea tan complejo imitar el comportamiento. A pesar de lo mucho que me gusta Django… :)

Tenéis más información en Managing static files (e.g. images, JavaScript, CSS) y Deploying static files.