python - digitalocean - gunicorn
¿Cómo administrar la configuración de producción local vs en Django? (20)
Administro mis configuraciones con la ayuda de django-split-settings .
Es un reemplazo directo de la configuración predeterminada. Es simple, pero configurable. Y no se requiere la refactorización de su configuración existente.
Aquí hay un pequeño ejemplo (ejemplo de archivo example/settings/__init__.py
):
from split_settings.tools import optional, include
import os
if os.environ[''DJANGO_SETTINGS_MODULE''] == ''example.settings'':
include(
''components/default.py'',
''components/database.py'',
# This file may be missing:
optional(''local_settings.py''),
scope=globals()
)
Eso es.
Actualizar
Escribí una entrada de blog sobre la gestión de la configuración de django-split-sttings
con django-split-sttings
. ¡Echar un vistazo!
¿Cuál es la forma recomendada de manejar las configuraciones para el desarrollo local y el servidor de producción? Algunos de ellos (como constantes, etc.) se pueden cambiar / acceder a ambos, pero algunos de ellos (como rutas a archivos estáticos) deben permanecer diferentes, y por lo tanto no deben sobrescribirse cada vez que se implementa el nuevo código.
Actualmente, estoy agregando todas las constantes a settings.py
. Pero cada vez que cambio alguna constante localmente, tengo que copiarlo en el servidor de producción y editar el archivo para cambios específicos de producción ... :(
Edición: parece que no hay una respuesta estándar a esta pregunta, he aceptado el método más popular.
El problema con la mayoría de estas soluciones es que tiene sus configuraciones locales aplicadas antes que las comunes o después de ellas.
Así que es imposible anular cosas como
- la configuración específica de env define las direcciones para el grupo de memcached, y en el archivo de configuración principal este valor se usa para configurar el backend de caché
- la configuración específica de env agrega o elimina apps / middleware a la predeterminada
al mismo tiempo.
Se puede implementar una solución utilizando archivos de configuración de estilo "ini" con la clase ConfigParser. Es compatible con múltiples archivos, interpolación de cadenas perezosa, valores predeterminados y muchas otras cosas. Una vez que se han cargado varios archivos, se pueden cargar más archivos y sus valores anularán a los anteriores, si los hay.
Carga uno o más archivos de configuración, dependiendo de la dirección de la máquina, las variables de entorno e incluso los valores en los archivos de configuración cargados anteriormente. Luego solo usa los valores analizados para completar la configuración.
Una estrategia que he usado con éxito ha sido:
- Cargar un archivo
defaults.ini
predeterminado - Verifique el nombre de la máquina y cargue todos los archivos que coincidan con el FQDN invertido, desde la coincidencia más corta hasta la coincidencia más larga (así,
net.ini
, luegonet.domain.ini
, luegonet.domain.webserver01.ini
, cada uno posiblemente anulando valores de los anteriores). Esta cuenta también para las máquinas de los desarrolladores, por lo que cada una podría configurar su controlador de base de datos preferido, etc. para el desarrollo local - Compruebe si hay un "nombre de clúster" declarado y, en ese caso, cargue
cluster.cluster_name.ini
, que puede definir cosas como bases de datos y direcciones IP de caché
Como ejemplo de algo que puede lograr con esto, puede definir un valor de "subdominio" por env, que luego se usa en la configuración predeterminada (como hostname: %(subdomain).whatever.net
) para definir todos los nombres de host necesarios y cosas de galleta que Django necesita para trabajar.
Esto es como DRY que pude obtener, la mayoría de los archivos (existentes) tenían solo 3 o 4 configuraciones. Además, tenía que administrar la configuración del cliente, por lo que existía un conjunto adicional de archivos de configuración (con nombres de bases de datos, usuarios y contraseñas, subdominio asignado, etc.), uno o más por cliente.
Uno puede escalar esto tan bajo o tan alto como sea necesario, simplemente coloque en el archivo de configuración las claves que desea configurar por entorno, y una vez que necesite una nueva configuración, coloque el valor anterior en la configuración predeterminada y anúltela donde sea necesario.
Este sistema ha demostrado ser confiable y funciona bien con el control de versiones. Se ha utilizado durante mucho tiempo para administrar dos grupos de aplicaciones independientes (15 o más instancias separadas del sitio de django por máquina), con más de 50 clientes, donde los grupos estaban cambiando de tamaño y miembros según el estado de ánimo del administrador del sistema. .
En settings.py
:
try:
from local_settings import *
except ImportError as e:
pass
Puede anular lo que se necesita en local_settings.py
; debería permanecer fuera de su control de versión entonces. Pero como mencionas copiar, supongo que no usas ninguno;)
En lugar de settings.py
, usa este diseño:
.
└── settings/
├── __init__.py <= not versioned
├── common.py
├── dev.py
└── prod.py
common.py
es donde vive la mayor parte de su configuración.
prod.py
importa todo lo común, y anula lo que sea necesario para anularlo:
from __future__ import absolute_import # optional, but I like it
from .common import *
# Production overrides
DEBUG = False
#...
De manera similar, dev.py
importa todo desde common.py
y reemplaza lo que sea necesario para anular.
Finalmente, __init__.py
es donde usted decide qué configuración cargar, y también es donde almacena secretos (por lo tanto, este archivo no debe ser versionado):
from __future__ import absolute_import
from .prod import * # or .dev if you want dev
##### DJANGO SECRETS
SECRET_KEY = ''(3gd6shenud@&57...''
DATABASES[''default''][''PASSWORD''] = ''f9kGH...''
##### OTHER SECRETS
AWS_SECRET_ACCESS_KEY = "h50fH..."
Lo que me gusta de esta solución es:
- Todo está en su sistema de versiones, excepto los secretos.
- La mayoría de la configuración está en un solo lugar:
common.py
. - Las cosas específicas de Prod van en
prod.py
, las cosas específicas dedev.py
van endev.py
Es sencillo. - Puede anular cosas de
common.py
enprod.py
odev.py
, y puede anular cualquier cosa en__init__.py
. - Es un pitón sencillo. No hay re-importación hacks.
Lo diferencié en manage.py y creé dos archivos de configuración separados: local_settings.py y prod_settings.py.
En manage.py compruebo si el servidor es servidor local o servidor de producción.Si se trata de un servidor local, se cargaría local_settings.py y si se trata de un servidor de producción, se cargaría prod_settings.py. Básicamente así es como se vería:
#!/usr/bin/env python
import sys
import socket
from django.core.management import execute_manager
ipaddress = socket.gethostbyname( socket.gethostname() )
if ipaddress == ''127.0.0.1'':
try:
import local_settings # Assumed to be in the same directory.
settings = local_settings
except ImportError:
import sys
sys.stderr.write("Error: Can''t find the file ''local_settings.py'' in the directory containing %r. It appears you''ve customized things./nYou''ll have to run django-admin.py, passing it your settings module./n(If the file local_settings.py does indeed exist, it''s causing an ImportError somehow.)/n" % __file__)
sys.exit(1)
else:
try:
import prod_settings # Assumed to be in the same directory.
settings = prod_settings
except ImportError:
import sys
sys.stderr.write("Error: Can''t find the file ''prod_settings.py'' in the directory containing %r. It appears you''ve customized things./nYou''ll have to run django-admin.py, passing it your settings module./n(If the file prod_settings.py does indeed exist, it''s causing an ImportError somehow.)/n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)
Descubrí que es más fácil separar el archivo de configuración en dos archivos separados en lugar de hacer muchos ifs dentro del archivo de configuración.
Mi solución a ese problema también es algo así como una mezcla de algunas soluciones que ya se mencionaron aquí:
-
local_settings.py
un archivo llamadolocal_settings.py
que tiene el contenidoUSING_LOCAL = True
in dev yUSING_LOCAL = False
in prod - En
settings.py
hago una importación en ese archivo para obtener la configuración deUSING_LOCAL
Luego baso todas mis configuraciones dependientes del entorno en esa:
DEBUG = USING_LOCAL
if USING_LOCAL:
# dev database settings
else:
# prod database settings
Prefiero esto a tener dos archivos settings.py separados que necesito mantener ya que puedo mantener mi configuración estructurada en un solo archivo más fácilmente que tenerlos repartidos en varios archivos. Así, cuando actualizo una configuración, no me olvido de hacerlo en ambos entornos.
Por supuesto que cada método tiene sus desventajas y este no es una excepción. El problema aquí es que no puedo sobrescribir el archivo local_settings.py
cuando local_settings.py
mis cambios en producción, lo que significa que no puedo copiar todos los archivos a ciegas, pero eso es algo con lo que puedo vivir.
Para la mayoría de mis proyectos utilizo el siguiente patrón:
- Crear settings_base.py donde almaceno configuraciones que son comunes para todos los entornos
- Siempre que necesito usar un nuevo entorno con requisitos específicos, creo un nuevo archivo de configuración (por ejemplo, settings_local.py) que hereda el contenido de settings_base.py y anula / agrega las variables de configuración adecuadas (
from settings_base import *
)
(Para ejecutar manage.py con el archivo de configuraciones personalizadas, simplemente use la opción de comando manage.py <command> --settings=settings_you_wish_to_use.py
: manage.py <command> --settings=settings_you_wish_to_use.py
)
Recuerda que settings.py es un archivo de código en vivo. Suponiendo que no haya establecido DEBUG en la producción (lo que es una buena práctica), puede hacer algo como:
if DEBUG:
STATIC_PATH = /path/to/dev/files
else:
STATIC_PATH = /path/to/production/files
Bastante básico, pero podría, en teoría, subir a cualquier nivel de complejidad basándose solo en el valor de DEBUG, o en cualquier otra variable o verificación de código que quisiera usar.
TL; DR: el truco consiste en modificar os.environment
antes de importar la settings/base.py
en cualquier settings/<purpose>.py
, esto simplificará enormemente las cosas.
El solo pensar en todos estos archivos entrelazados me da un dolor de cabeza. Combinar, importar (a veces condicionalmente), anular, parchear lo que ya se estableció en caso de que la configuración de DEBUG
se modificara más adelante. ¡Qué pesadilla!
A través de los años pasé por todas las diferentes soluciones. Todos funcionan un poco , pero son tan dolorosos de manejar. WTF! ¿Realmente necesitamos toda esa molestia? Comenzamos con un solo archivo settings.py
. ¡Ahora necesitamos una documentación para combinar correctamente todo esto en un orden correcto!
Espero finalmente alcanzar el (mi) punto dulce con la solución a continuación.
Recapitulemos los objetivos (algunos comunes, algunos míos)
Mantenga los secretos en secreto, no los guarde en un repositorio.
Configure / lea claves y secretos a través de la configuración del entorno, estilo de 12 factores .
Tener valores predeterminados de reserva razonables. Idealmente para el desarrollo local, no necesita nada más aparte de los valores predeterminados.
... pero trate de mantener la producción por defecto segura. Es mejor pasar por alto una anulación de configuración localmente, que tener que recordar ajustar la configuración predeterminada segura para la producción.
Tiene la capacidad de activar / desactivar
DEBUG
de una manera que puede tener un efecto en otras configuraciones (por ejemplo, usando javascript comprimido o no).Cambiar entre configuraciones de propósito, como local / testing / staging / production, debe basarse solo en
DJANGO_SETTINGS_MODULE
, nada más.... pero permite una mayor parametrización a través de la configuración del entorno como
DATABASE_URL
.... también les permite usar diferentes configuraciones de propósito y ejecutarlas localmente lado a lado, por ejemplo. Configuración de la producción en la máquina del desarrollador local, para acceder a la base de datos de producción o a las hojas de estilo de prueba de humo comprimidas.
Fallo si una variable de entorno no se establece explícitamente (lo que requiere un valor vacío como mínimo), especialmente en producción, por ejemplo.
EMAIL_HOST_PASSWORD
.Responda al
DJANGO_SETTINGS_MODULE
predeterminado establecido en manage.py durante django-admin startprojectMantenga las condiciones al mínimo, si la condición es el tipo de entorno propuesto (por ejemplo, para el archivo de registro del conjunto de producción y su rotación), anule las configuraciones en el archivo de configuraciones asociadas.
No hacer
No permita que django lea el ajuste DJANGO_SETTINGS_MODULE desde un archivo.
Ugh! Piensa en qué meta es esto. Si necesita tener un archivo (como docker env), lea eso en el entorno antes de iniciar un proceso de django.No invalide DJANGO_SETTINGS_MODULE en su proyecto / código de aplicación, por ejemplo. basado en nombre de host o nombre de proceso.
Si es perezoso para establecer una variable de entorno (como para lasetup.py test
), hágalo en las herramientas justo antes de ejecutar el código del proyecto.Evite la magia y la aplicación de parches de cómo django lee sus configuraciones, preprocesar las configuraciones pero no interferir después.
Sin lógica complicada basada en tonterías. La configuración debe ser fija y materializada, no computada sobre la marcha. Proporcionar una configuración de respaldo es solo suficiente lógica aquí.
¿Realmente desea depurar, por qué localmente tiene el conjunto correcto de configuraciones pero en producción en un servidor remoto, en una de cien máquinas, algo computado de manera diferente? Oh! Pruebas unitarias? Para ajustes? ¿Seriamente?
Solución
Mi estrategia consiste en un excelente django-environ usado con archivos de estilo ini
, que proporciona os.environment
predeterminados de os.environment
para el desarrollo local, algunas settings/<purpose>.py
mínimas y cortas settings/<purpose>.py
archivos settings/<purpose>.py
que tienen una import settings/base.py
DESPUÉS de que fuera os.environment
establecer desde un archivo INI
. Esto efectivamente nos da una especie de inyección de ajustes.
El truco aquí es modificar os.environment
antes de importar la settings/base.py
Para ver el ejemplo completo, haga el repo: https://github.com/wooyek/django-settings-strategy
.
│ manage.py
├───data
└───website
├───settings
│ │ __init__.py <-- imports local for compatybility
│ │ base.py <-- almost all the settings, reads from proces environment
│ │ local.py <-- a few modifications for local development
│ │ production.py <-- ideally is empy and everything is in base
│ │ testing.py <-- mimics production with a reasonable exeptions
│ │ .env <-- for local use, not kept in repo
│ __init__.py
│ urls.py
│ wsgi.py
configuraciones / .env
Un defecto para el desarrollo local. Un archivo secreto, para establecer principalmente las variables de entorno requeridas. Establézcalos en valores vacíos si no son necesarios en el desarrollo local. Proporcionamos valores predeterminados aquí y no en settings/base.py
para fallar en cualquier otra máquina si faltan en el entorno.
configuración / local.py
Lo que sucede aquí es cargar el entorno desde la settings/.env
, y luego importar la configuración común desde la settings/base.py
Después de eso podemos anular algunos para facilitar el desarrollo local.
import logging
import environ
logging.debug("Settings loading: %s" % __file__)
# This will read missing environment variables from a file
# We wan to do this before loading a base settings as they may depend on environment
environ.Env.read_env(DEBUG=''True'')
from .base import *
ALLOWED_HOSTS += [
''127.0.0.1'',
''localhost'',
''.example.com'',
''vagrant'',
]
# https://docs.djangoproject.com/en/1.6/topics/email/#console-backend
EMAIL_BACKEND = ''django.core.mail.backends.console.EmailBackend''
# EMAIL_BACKEND = ''django.core.mail.backends.dummy.EmailBackend''
LOGGING[''handlers''][''mail_admins''][''email_backend''] = ''django.core.mail.backends.dummy.EmailBackend''
# Sync task testing
# http://docs.celeryproject.org/en/2.5/configuration.html?highlight=celery_always_eager#celery-always-eager
CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
settings / production.py
Para producción, no debemos esperar un archivo de entorno, pero es más fácil tener uno si estamos probando algo. Pero de todos modos, para que no se proporcionen algunos valores predeterminados en línea, la settings/base.py
puede responder en consecuencia.
environ.Env.read_env(Path(__file__) / "production.env", DEBUG=''False'', ASSETS_DEBUG=''False'')
from .base import *
El principal punto de interés aquí son las modificaciones de DEBUG
y ASSETS_DEBUG
, se aplicarán al python os.environ
SOLAMENTE si FALTAN del entorno y del archivo.
Estos serán nuestros valores predeterminados de producción, no es necesario colocarlos en el entorno o archivo, pero se pueden anular si es necesario. ¡Ordenado!
ajustes / base.py
Estas son las configuraciones en su mayoría de vainilla django, con algunos condicionales y muchos de los que se leen desde el entorno. Casi todo está aquí, manteniendo todos los entornos propuestos constantes y lo más similares posible.
Las principales diferencias son a continuación (espero que sean auto explicativas):
import environ
# https://github.com/joke2k/django-environ
env = environ.Env()
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Where BASE_DIR is a django source root, ROOT_DIR is a whole project root
# It may differ BASE_DIR for eg. when your django project code is in `src` folder
# This may help to separate python modules and *django apps* from other stuff
# like documentation, fixtures, docker settings
ROOT_DIR = BASE_DIR
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env(''SECRET_KEY'')
# SECURITY WARNING: don''t run with debug turned on in production!
DEBUG = env(''DEBUG'', default=False)
INTERNAL_IPS = [
''127.0.0.1'',
]
ALLOWED_HOSTS = []
if ''ALLOWED_HOSTS'' in os.environ:
hosts = os.environ[''ALLOWED_HOSTS''].split(" ")
BASE_URL = "https://" + hosts[0]
for host in hosts:
host = host.strip()
if host:
ALLOWED_HOSTS.append(host)
SECURE_SSL_REDIRECT = env.bool(''SECURE_SSL_REDIRECT'', default=False)
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
if "DATABASE_URL" in os.environ: # pragma: no cover
# Enable database config through environment
DATABASES = {
# Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
''default'': env.db(),
}
# Make sure we use have all settings we need
# DATABASES[''default''][''ENGINE''] = ''django.contrib.gis.db.backends.postgis''
DATABASES[''default''][''TEST''] = {''NAME'': os.environ.get("DATABASE_TEST_NAME", None)}
DATABASES[''default''][''OPTIONS''] = {
''options'': ''-c search_path=gis,public,pg_catalog'',
''sslmode'': ''require'',
}
else:
DATABASES = {
''default'': {
''ENGINE'': ''django.db.backends.sqlite3'',
# ''ENGINE'': ''django.contrib.gis.db.backends.spatialite'',
''NAME'': os.path.join(ROOT_DIR, ''data'', ''db.dev.sqlite3''),
''TEST'': {
''NAME'': os.path.join(ROOT_DIR, ''data'', ''db.test.sqlite3''),
}
}
}
STATIC_ROOT = os.path.join(ROOT_DIR, ''static'')
# django-assets
# http://django-assets.readthedocs.org/en/latest/settings.html
ASSETS_LOAD_PATH = STATIC_ROOT
ASSETS_ROOT = os.path.join(ROOT_DIR, ''assets'', "compressed")
ASSETS_DEBUG = env(''ASSETS_DEBUG'', default=DEBUG) # Disable when testing compressed file in DEBUG mode
if ASSETS_DEBUG:
ASSETS_URL = STATIC_URL
ASSETS_MANIFEST = "json:{}".format(os.path.join(ASSETS_ROOT, "manifest.json"))
else:
ASSETS_URL = STATIC_URL + "assets/compressed/"
ASSETS_MANIFEST = "json:{}".format(os.path.join(STATIC_ROOT, ''assets'', "compressed", "manifest.json"))
ASSETS_AUTO_BUILD = ASSETS_DEBUG
ASSETS_MODULES = (''website.assets'',)
El último bit muestra el poder aquí. ASSETS_DEBUG
tiene un valor predeterminado razonable, que se puede anular en settings/production.py
e incluso eso se puede anular con una configuración de entorno. ¡Hurra!
En efecto tenemos una jerarquía mixta de importancia:
- settings / .py - establece los valores predeterminados según el propósito, no almacena secretos
- settings / base.py - está controlado principalmente por el entorno
- Configuración del entorno de proceso - ¡12 factores, bebé!
- settings / .env - valores predeterminados locales para un inicio fácil
También estoy trabajando con Laravel y me gusta la implementación allí. Intenté imitarlo y combinándolo con la solución propuesta por T. Stone (ver arriba):
PRODUCTION_SERVERS = [''*.webfaction.com'',''*.whatever.com'',]
def check_env():
for item in PRODUCTION_SERVERS:
match = re.match(r"(^." + item + "$)", socket.gethostname())
if match:
return True
if check_env():
PRODUCTION = True
else:
PRODUCTION = False
DEBUG = not PRODUCTION
Tal vez algo como esto te ayude.
También hay Django Classy Settings. Yo personalmente soy un gran fan de ella. Está construido por una de las personas más activas en el IRC de Django. Usted usaría las variables del entorno para establecer las cosas.
Uso una versión ligeramente modificada del estilo de configuración "if DEBUG" que publicó Harper Shelby. Obviamente, dependiendo del entorno (win / linux / etc.) Es posible que el código deba modificarse un poco.
Yo estaba en el pasado usando "DEBUG", pero encontré que ocasionalmente necesitaba hacer pruebas con DEUBG en Falso. Lo que realmente quería distinguir si el entorno era la producción o el desarrollo, lo que me dio la libertad de elegir el nivel DEBUG.
PRODUCTION_SERVERS = [''WEBSERVER1'',''WEBSERVER2'',]
if os.environ[''COMPUTERNAME''] in PRODUCTION_SERVERS:
PRODUCTION = True
else:
PRODUCTION = False
DEBUG = not PRODUCTION
TEMPLATE_DEBUG = DEBUG
# ...
if PRODUCTION:
DATABASE_HOST = ''192.168.1.1''
else:
DATABASE_HOST = ''localhost''
Todavía consideraría esta forma de configuración un trabajo en progreso. No he visto ninguna manera de manejar la configuración de Django que cubriera todas las bases y, al mismo tiempo, no fue un problema total de configuración (no estoy familiarizado con los métodos de archivos de configuración de 5x).
Utilizo settings_local.py y settings_production.py. Después de probar varias opciones, descubrí que es fácil perder el tiempo con soluciones complejas cuando simplemente tener dos archivos de configuración es fácil y rápido.
Cuando use mod_python / mod_wsgi para su proyecto de Django, debe apuntarlo a su archivo de configuración. Si lo apunta a app / settings_local.py en su servidor local y app / settings_production.py en su servidor de producción, entonces la vida se vuelve fácil. Solo edite el archivo de configuración apropiado y reinicie el servidor (el servidor de desarrollo de Django se reiniciará automáticamente).
Utilizo una variación de lo que jpartogi mencionó anteriormente, que encuentro un poco más corto:
import platform
from django.core.management import execute_manager
computername = platform.node()
try:
settings = __import__(computername + ''_settings'')
except ImportError:
import sys
sys.stderr.write("Error: Can''t find the file ''%r_settings.py'' in the directory containing %r. It appears you''ve customized things./nYou''ll have to run django-admin.py, passing it your settings module./n(If the file local_settings.py does indeed exist, it''s causing an ImportError somehow.)/n" % (computername, __file__))
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)
Básicamente, en cada computadora (desarrollo o producción) tengo el archivo hostname_settings.py apropiado que se carga dinámicamente.
1 - Crea una nueva carpeta dentro de tu aplicación y configura su nombre.
2 - Ahora crea un nuevo archivo init .py en él y dentro escribe.
from .base import *
try:
desde .local import *
except:
pasar
try:
de .production import *
except:
pasar
3 - Cree tres archivos nuevos en el nombre de la carpeta de configuración local.py y production.py y base.py
4 - Dentro de base.py, copie todo el contenido de la carpeta settings.p anterior y cambie su nombre con algo diferente, digamos old_settings.py
5 - En base.py cambie su ruta BASE_DIR para que apunte a su nueva ruta de configuración
Ruta anterior-> BASE_DIR = os.path.dirname (os.path.dirname (os.path.abspath ( archivo )))
Nueva ruta -> BASE_DIR = os.path.dirname (os.path.dirname (os.path.dirname (os.path.abspath ( archivo )))
ahora, de esta manera, el directorio del proyecto puede ser estructurado y manejable entre la producción y el desarrollo local.
Two Scoops of Django: Best Practices for Django 1.5 sugiere usar el control de versiones para tus archivos de configuración y almacenar los archivos en un directorio separado:
project/
app1/
app2/
project/
__init__.py
settings/
__init__.py
base.py
local.py
production.py
manage.py
El archivo base.py
contiene configuraciones comunes (como MEDIA_ROOT o ADMIN), mientras que local.py
y production.py
tienen configuraciones específicas del sitio:
En la settings/base.py
archivo base settings/base.py
:
INSTALLED_APPS = (
# common apps...
)
En la configuración del archivo de settings/local.py
desarrollo local settings/local.py
:
from project.settings.base import *
DEBUG = True
INSTALLED_APPS += (
''debug_toolbar'', # and other apps for local development
)
En la configuración de archivo de settings/production.py
archivo settings/production.py
:
from project.settings.base import *
DEBUG = False
INSTALLED_APPS += (
# other apps for production site
)
Luego, cuando ejecutas django, agregas la opción --settings
:
# Running django for local development
$ ./manage.py runserver 0:8000 --settings=project.settings.local
# Running django shell on the production site
$ ./manage.py shell --settings=project.settings.production
Los autores del libro también han presentado una plantilla de diseño de proyecto de muestra en Github.
Como alternativa, puede mantener archivos diferentes: si está utilizando git o cualquier otro VCS para enviar códigos del local al servidor, puede agregar el archivo de configuración a .gitignore.
Esto te permitirá tener contenido diferente en ambos lugares sin ningún problema. SO en el servidor puede configurar una versión independiente de settings.py y cualquier cambio realizado en el local no se reflejará en el servidor y viceversa.
Además, también eliminará el archivo settings.py de github, la gran falla, que he visto hacer a muchos novatos.
Tenía mi configuración dividida de la siguiente manera
settings/
|
|- base.py
|- dev.py
|- prod.py
Tenemos 3 ambientes
- dev
- puesta en escena
- producción
Ahora, obviamente, la puesta en escena y la producción deben tener el máximo entorno similar posible. Así que nos quedamos prod.py
con los dos.
Pero hubo un caso en el que tuve que identificar que el servidor en ejecución es un servidor de producción. @T. La respuesta de Stone me ayudó a escribir el cheque de la siguiente manera.
from socket import gethostname, gethostbyname
PROD_HOSTS = ["webserver1", "webserver2"]
DEBUG = False
ALLOWED_HOSTS = [gethostname(), gethostbyname(gethostname()),]
if any(host in PROD_HOSTS for host in ALLOWED_HOSTS):
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
He encontrado las respuestas aquí muy útiles. (¿Se ha resuelto esto de manera más definitiva? La última respuesta fue hace un año). Después de considerar todos los enfoques enumerados, se me ocurrió una solución que no vi en esta lista.
Mis criterios fueron:
- Todo debe estar en control de la fuente. No me gustan los pedacitos inquietos por ahí.
- Lo ideal es mantener la configuración en un archivo. Me olvido de las cosas si no las estoy mirando directamente :)
- No hay ediciones manuales para desplegar. Debe poder probar / empujar / desplegar con un solo comando de estructura.
- Evitar fugas de configuración de desarrollo en producción.
- Manténgase lo más cerca posible del diseño Django "estándar" (* tos *) que sea posible.
Pensé que encender el equipo host tenía algún sentido, pero luego me di cuenta de que el verdadero problema aquí es la configuración diferente para diferentes entornos , y tuve un momento aha. Pongo este código al final de mi archivo settings.py:
try:
os.environ[''DJANGO_DEVELOPMENT_SERVER''] # throws error if unset
DEBUG = True
TEMPLATE_DEBUG = True
# This is naive but possible. Could also redeclare full app set to control ordering.
# Note that it requires a list rather than the generated tuple.
INSTALLED_APPS.extend([
''debug_toolbar'',
''django_nose'',
])
# Production database settings, alternate static/media paths, etc...
except KeyError:
print ''DJANGO_DEVELOPMENT_SERVER environment var not set; using production settings''
De esta manera, la aplicación se configura de manera predeterminada en la configuración de producción, lo que significa que está explícitamente "en la lista blanca" de su entorno de desarrollo. Es mucho más seguro olvidarse de establecer la variable de entorno localmente que si fuera al revés y se olvidó de establecer algo en producción y dejar que se usen algunas configuraciones de desarrollo.
Cuando se desarrolla localmente, ya sea desde el shell o en un .bash_profile o donde sea:
$ export DJANGO_DEVELOPMENT_SERVER=yep
(O si está desarrollando en Windows, configúrelo a través del Panel de control o como se llame en estos días ... Windows siempre lo hizo tan oscuro que podría establecer variables de entorno).
Con este enfoque, las configuraciones de desarrollo se encuentran en un solo lugar (estándar), y simplemente anulan las de producción donde sea necesario. Cualquier desperdicio con las configuraciones de desarrollo debe ser completamente seguro comprometerse con el control de la fuente sin impacto en la producción.
Para utilizar una settings
configuración diferente en un entorno diferente, cree un archivo de configuración diferente. Y en su script de implementación, inicie el servidor usando un --settings=<my-settings.py>
parámetro, a través del cual puede usar diferentes configuraciones en diferentes entornos.
Beneficios de utilizar este enfoque :
Sus configuraciones serán modulares basadas en cada entorno.
Puede importar el que
master_settings.py
contiene la configuración básica enenvironmnet_configuration.py
y anular los valores que desea cambiar en ese entorno.Si tiene un gran equipo, cada desarrollador puede tener el suyo propio,
local_settings.py
que puede agregar al repositorio de códigos sin ningún riesgo de modificar la configuración del servidor. Puede agregar estas configuraciones locales a.gitnore
si usa git o.hginore
si es Mercurial para el control de versiones (o cualquier otro). De ese modo, la configuración local ni siquiera formará parte de la base de código real y lo mantendrá limpio.