Servicio de archivos estáticos en Python con Django, AWS S3 y WhiteNoise

En este artículo, echamos un vistazo a cómo servir archivos estáticos usando Django, incluidos ejemplos de cómo servir archivos estáticos localmente y en producción usando AWS S3 y WhiteNoise.

Introducción

Los sitios web generalmente necesitan archivos adicionales como imágenes, CSS y archivos JavaScript que son necesarios para mostrar páginas web completas en un navegador. En proyectos pequeños, podemos trabajar proporcionando rutas absolutas a nuestros recursos o escribiendo funciones CSS y JavaScript en línea en los archivos HTML. Esto no solo va en contra de las mejores prácticas de codificación, sino que también se vuelve complicado cuando manejamos proyectos más grandes, especialmente con múltiples aplicaciones.

En Django, los archivos necesarios para la experiencia interactiva del usuario, la presentación de documentos y las páginas web funcionales se denominan archivos estáticos.

En este artículo, veremos cómo podemos manejar múltiples conjuntos de archivos estáticos proporcionados por cada aplicación para personalizar la apariencia de un sitio web.

Configuración de archivos estáticos {#configuración de archivos estáticos}

Django proporciona una gran flexibilidad sobre cómo puede servir los archivos estáticos. Cubriremos el uso de archivos estáticos en el desarrollo local, así como en producción, que es un poco más complejo. Lo primero es lo primero, hagamos la configuración básica.

Django proporciona django.contrib.archivos estáticos para ayudarlo a recopilar archivos estáticos de cada uno de sus aplicaciones (y cualquier otro lugar que especifique) en una sola ubicación que se puede servir fácilmente en producción.

En su archivo settings.py, sus INSTALLED_APPS deberían verse así:

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.sites',
    'django.contrib.contenttypes',
    'django.contrib.admin',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles', # To serve static files
]

STATIC_ROOT es la ruta que define dónde se recopilarán sus archivos estáticos. Proporcionaremos una ruta absoluta a STATIC_ROOT en settings.py.

Para hacer esto, usaremos la función dirname() del módulo os para obtener el nombre del directorio en el que nos gustaría alojar estos archivos y definir la ruta:

1
2
3
4
import os

PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles')

Luego, debe especificar una STATIC_URL, que es la URL utilizada cuando se hace referencia a archivos estáticos. Debe terminar con / si se establece en cualquier valor excepto Ninguno. La siguiente ruta significa que los archivos estáticos se almacenarán en la ubicación http://localhost:8000/static/ o http://127.0.0.1:8000/static/:

1
STATIC_URL = '/static/'

Django tiene una lista de buscadores como STATICFILES_FINDERS que utiliza para localizar archivos estáticos. Uno de los buscadores predeterminados es AppDirectoriesFinder que busca una carpeta llamada static dentro de cada una de sus INSTALLED_APPS.

Por ejemplo, si su proyecto contiene una aplicación llamada users, puede crear un directorio como project_name/users/static/index.css para agregar archivos CSS relacionados con esa aplicación.

Aunque esto funciona, es una mejor idea crear otro subdirectorio con el nombre de su aplicación, como nombre_del_proyecto/usuarios/estático/usuarios/index.css. Esto es importante cuando tenemos dos o más archivos estáticos con nombres similares.

Consideremos que tiene un index.css en cada aplicación, cada una de las cuales contiene diferentes estilos CSS. Django buscará el primer index.css que pueda encontrar en los directorios app/static/. No podrá distinguir entre varios index.css que tenemos en el directorio static de cada aplicación. Es por eso que creamos un subdirectorio con el nombre de la aplicación app/static/app/.

Además, la mayoría de los proyectos tienen varias aplicaciones que pueden tener archivos estáticos comunes, por lo que normalmente es mejor crear una carpeta “estática” en el directorio raíz de su proyecto en lugar de crear una carpeta “estática” en cada aplicación:

static folder directory

Para usar un lugar común para todos los archivos estáticos en el directorio de su proyecto, necesitamos configurar STATICFILES_DIRS para informar a Django sobre nuestro nuevo directorio porque AppDirectoriesFinder buscará static solo en los directorios de app. También podemos definir múltiples ubicaciones para nuestros archivos estáticos.

Este es el lugar para definir las carpetas estáticas de proyectos individuales si tiene varias:

1
2
3
4
STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT, 'static'),
    # Extra lookup directories for collectstatic to find static files
)

Tenga en cuenta que STATICFILES_DIRS solo funcionará si no elimina FileSystemFinder de STATICFILES_FINDERS.

Como breve resumen, nuestro settings.py incluye:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import os

PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
STATIC_ROOT  = os.path.join(PROJECT_ROOT, 'staticfiles')
STATIC_URL = '/static/'

# Extra lookup directories for collectstatic to find static files
STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT, 'static'),
)

Los archivos estáticos están listos para ser utilizados en su proyecto. Solo necesitamos cargar la etiqueta de plantilla estática con {% load static %} y luego usar la etiqueta de plantilla static para construir la URL para la ruta relativa dada. Veamos cómo podemos usar archivos estáticos en nuestro archivo de plantilla base.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!doctype html>
{% load static %}
<html lang="en">
    {% include 'head.html' %}
 <style>
    body{
      background: url('{% static "bg.png" %}') no-repeat center center fixed; 
        -webkit-background-size: cover;
        -moz-background-size: cover;
        -o-background-size: cover;
        background-size: cover;
    }
 </style>
  <body>
      <div class="row justify-content-center">
        <div class="col-8">
            <h1 class="mainbtn">MY CUSTOM CSS CLASS</h1>
          {% block content %}
          <hr class="mt-0 mb-4">
          {% endblock %}
        </div>
      </div>
    </div>
  </body>
</html>

El base.html incluye una plantilla head.html para una segregación adecuada, ya que los proyectos más grandes suelen contener código extenso en las etiquetas head. La clase mainbtn para h1 está definida en el archivo static/index.css. La imagen de fondo bg.png también está presente en el directorio static.

El head.html se ve así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<head>
    {% block css_block %}{% endblock %}
    {% load static %}
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    
    <link rel="stylesheet" href="{% static 'css/index.css' %}">
    <script src="{% static 'js/functions.js' %}"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    
    <title>{% block title %} Title to be changed in included files {% endblock %}</title>
</head>

Servir archivos estáticos

Además de las configuraciones anteriores, también necesitamos servir los archivos estáticos. Se realiza automáticamente mediante el comando runserver de Django si Debug = True. Debe usar este método en la fase de desarrollo ya que es fácil, sin embargo, no se recomienda para la producción porque es ineficiente e inseguro.

Django viene con un comando incorporado collecstatic. Compila todos los archivos estáticos en un solo directorio STATIC_ROOT que ya configuramos. La pieza final es el motor de almacenamiento que se utiliza al recopilar archivos estáticos con el comando collectstatic. El motor de almacenamiento se puede configurar mediante STATICFILES_STORAGE. Django tiene su propio motor de almacenamiento, por lo que el valor predeterminado de STATICFILES_STORAGE se establece en django.contrib.staticfiles.storage.StaticFilesStorage.

Archivos estáticos en producción

Hay dos pasos principales para colocar archivos estáticos en un entorno de producción:

  • Ejecute el comando collectstatic cada vez que cambien los archivos estáticos
  • Haga arreglos para que STATIC_ROOT se mueva al servidor de archivos estáticos y se sirva

El método post_process() de la clase Storage puede encargarse del segundo paso, pero realmente depende de su motor de almacenamiento, es decir, STATICFILES_STORAGE.

Nota: Debe saber que servir archivos estáticos en cada producción será diferente debido a la diferencia en los entornos, pero la idea básica y los pasos siguen siendo los mismos. Hay tres tácticas principales para manejar los archivos estáticos en producción:

  • Servir los archivos estáticos y el sitio desde el mismo servidor: Utilice este método si desea que sus archivos estáticos se sirvan desde el servidor que ya está ejecutando su aplicación web. A pesar de su posible problema de rendimiento, podría ser rentable ya que solo necesita pagar por el alojamiento de un servidor. Para hacer esto, envíe su código al servidor de implementación y luego ejecute collectstatic para copiar todos los archivos a STATIC_ROOT. Por último, configure su servidor web para servir los archivos estáticos bajo STATIC_URL.

  • Servir archivos estáticos desde un servidor dedicado: Las opciones más comunes para servidores de archivos estáticos dedicados son nginx y [versión simplificada de Apache](https:// httpd.apache.org/). La aplicación web se ejecuta en un servidor completamente diferente, mientras que sus archivos estáticos se implementan en un servidor dedicado que brinda un rendimiento general más rápido. Ejecute collectstatic localmente cada vez que cambien los archivos estáticos y luego inserte STATIC_ROOT en el directorio de su servidor dedicado que se está sirviendo. Para obtener instrucciones detalladas, debe consultar la documentación del servidor respectivo.

  • Servir archivos estáticos desde un servicio en la nube: Otra táctica común es servir archivos estáticos desde un proveedor de almacenamiento en la nube como Amazon, Microsoft Azure y Alibaba Cloud.

Veamos cómo podemos usar Amazonas S3 para este propósito. Primero, instale dos bibliotecas de Python usando estos comandos:

1
2
$ python -m pip install boto3
$ pip install django-storages

La biblioteca boto3 es un cliente API público para acceder a Amazon S3 y otros servicios web de Amazon (AWS). Django-storages administra backends de almacenamiento como Amazon S3, OneDrive, etc. Se conecta a la API de backend de almacenamiento de Django incorporada. También deberá agregar almacenamiento en sus INSTALLED_APPS. Nuestro INSTALLED_APPS se ve así ahora:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.sites',
    'django.contrib.contenttypes',
    'django.contrib.admin',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
    'storages', # New
]

Después de eso, agregue las siguientes configuraciones en su settings.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
AWS_ACCESS_KEY_ID = your_access_key_id
AWS_SECRET_ACCESS_KEY = your_secret_access_key
AWS_STORAGE_BUCKET_NAME = 'sibtc-static'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
  
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

Finalmente, ejecute python manage.py collectstatic y habrá terminado de configurar Amazon S3 para sus archivos estáticos.

Sirviendo archivos estáticos usando WhiteNoise

Las personas a menudo no usan servicios en la nube de terceros como Amazon S3 por un par de razones, incluidas las suscripciones pagas. WhiteNoise permite que su proyecto Django sirva sus propios archivos estáticos, lo que lo convierte en una unidad autónoma que podemos implementar en cualquier lugar sin depender de los proveedores de servicios.

Aunque funciona con cualquier aplicación web compatible con WSGI, se configura más fácilmente con un proyecto Django.

Configuración para WhiteNoise

Instalemos Ruido blanco con:

1
$ pip install whitenoise

En su settings.py, agregue WhiteNoise a la lista MIDDLEWARE en el siguiente orden:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # WhiteNoise Middleware above all but below Security
    'whitenoise.middleware.WhiteNoiseMiddleware', 
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
  ]

Para usar el soporte de compresión y los archivos almacenables en caché para siempre, agregue esto en su settings.py
STATICFILES_STORAGE = 'ruido blanco.almacenamiento.CompressedManifestStaticFilesStorage'

Ejecute python manage.py collectstatic.

¡Eso es todo! Ahora puede implementar su aplicación web en cualquier plataforma de hospedaje como Heroku.

Conclusión

Cada desarrollador de sitios web necesita archivos estáticos para crear un sitio web hermoso y funcional. Django no solo ofrece una fácil configuración de archivos estáticos, sino también una gran flexibilidad para jugar con su implementación.

En este artículo, cubrimos varias formas de integrar archivos estáticos en una aplicación web de Django tanto en el desarrollo local como en la producción. n la producción.