Gestionando varios entornos de configuración en Django
Cuando se trabaja en un proyecto de Django lo suficientemente grande que se espera que se desarrolle durante un tiempo, la necesidad de administrar configuraciones en diferentes entornos se hace evidente rápidamente. En este post discutimos cómo se pueden manejar estas diferencias para que la repetición sea mínima. Aprovechamos los módulos de la librería estandar de Python y el sistema de importación para implementar configuraciones en cascada sin introducir nuevas dependencias de proyectos y sin romper la funcionalidad conf.settings de Django.
devops django
El objetivo de este artículo es conseguir que una aplicación Django cambie su configuración dependiendo del entorno en el que se esté ejecutando (producción, staging, local o desarrollo). También nos aseguraremos de seguir las buenas prácticas al tratar con los secretos de configuración (por ejemplo, claves de APIs) e intentaremos mantener nuestra configuración lo más DRY (Don't Repeat Yourself) posible. Comenzamos averiguando cómo funciona el sistema de configuración de Django y cuáles son sus componentes y construimos a partir de las herramientas que nos brinda para llegar a un punto en el que podemos cambiar la configuración cambiando el valor de una variable de entorno.
Configuración estándar
Al crear un proyecto Django con django-admin startproject awesome_app, obtenemos la siguiente estructura de carpetas
.
├── awesome_app
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
El archivo awesome_app/settings.py
contiene la configuración del proyecto. Django sabe qué archivo contiene la configuración del proyecto leyendo la variable de entorno DJANGO_SETTINGS_MODULE
, que se establece en awesome_app.settings por defecto. Como puedes haber adivinado, el valor de esta variable debe ser el nombre de un módulo de Python.
Este archivo configura cómo Django se conecta a su base de datos, qué aplicaciones se cargan y más a través de variables de Python. Aquí tienes una lista completa de las configuraciones que acepta Django. Como ejemplo, si el soporte de zona horaria está habilitado se determina por la variable USE_TZ
que se establece inicialmente en True
cuando django-admin
genera tu proyecto. Además, es posible leer estas variables por sí mismo y cambiar el comportamiento de su aplicación. La forma recomendada de hacerlo es importando django.conf.settings
y leyendo las variables de allí, en lugar de codificar el nombre de tu aplicación, ya que esto hace que tu código sea más reusable.
Estas variables no tienen que ser valores estáticos, podrías leerlos desde el entorno a través de os.environ
más o menos así:
import os
USE_TZ = os.environ.get('USE_TZ', True)
Esto es especialmente útil para asegurarse de que los secretos no se comprueban en el control de versiones, por ejemplo, si estuviéramos conectando a una base de datos protegida por contraseña, podríamos recuperar la contraseña de una variable de entorno en lugar de asignarla directamente a DATABASES['default']['PASSWORD']
. De esta manera, solo las personas responsables de desplegar nuestra aplicación tienen acceso a esa contraseña. De hecho, una forma de establecer estas variables de entorno es mediante export
si se ejecuta la aplicación desde una shell, estableciéndolas en un archivo docker-compose
o utilizando algo como ](https://github.com/jpadilla/django-dotenv) para leerlas automáticamente desde un archivo .env
que debe ser excluido del control de versiones.
Usando módulos múltiples para diferentes entornos
El enfoque anterior de leer valores del entorno ciertamente puede utilizarse para configurar una aplicación para funcionar de manera diferente dependiendo de si estamos en producción o desarrollo. Sin embargo, no todas las configuraciones tienen que ser secretas, es posible que queramos mantener algunas de ellas en control de versiones, y a los desarrolladores que usan Windows no les gusta especialmente jugar con archivos que comienzan con ".". Sin embargo, ya que Django espera un módulo de Python como la fuente de su configuración, podemos simplemente copiar la configuración a archivos separados para cada entorno que deseemos configurar y establecer la variable de entorno DJANGO_SETTINGS_MODULE
(y cualquier otra variable que contenga secretos).
Para hacerlo, crea una carpeta llamada settings y un archivo __init__.py
dentro de ella (para convertirlo en un módulo de Python). Luego renombra el archivo settings.py
a settings/common.py
. Esto nos deja con la siguiente estructura de directorios:
.
├── awesome_app
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings
│ │ ├── common.py
│ │ ├── development.py
│ │ ├── __init__.py
│ │ ├── production.py
│ │ └── staging.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
Observa que hemos añadido los archivos development.py
, staging.py
y production.py
a la carpeta settings
. Esto archivos solamente contienen la línea form .common import *
y cualquier variable que queramos sobrescribir del archivo common.py
. Por ejemplo, si queremos cambiar el host de la base de datos y desactivar el modo de depuración en producción podríamos poner lo siguiente en el archivo production.py
:
from .common import *
DATABASE['default']['HOST'] = '1.2.3.4'
DEBUG = False
Luego, para indicarle a Django que utilice otro archivo de configuración, simplemente tenemos que cambiar el valor de DJANGO_SETTINGS_MODULE
en el entorno. Por ejemplo, si queremos utilizar la configuración de desarrollo, establecemos DJANGO_SETTINGS_MODULE=awesome_app.settings.development
. De esta manera, podemos tener configuraciones separadas para producción, staging, desarrollo local y más, y cambiarlas fácilmente cambiando el valor de una variable de entorno.
De esta manera, podemos mantener toda la configuración junta en common.py
y sobrescribir solo lo que necesitemos en cada entorno. Recuerda seguir utilizando variables de entorno para configuraciones que deban ser secretas como claves para APIs, ya que estas no deberían incluirse en control de versions.
Por último, para decirle a Django qué configuración utilizar, recuerda configurar la variable de entorno DJANGO_SETTINGS_MODULE
antes de ejecutar tu aplicación. En sistemas POSIX esto se puede hacer con
export DJANGO_SETTINGS_MODULE=awesome_app.settings.development