¿Cómo administrar la configuración de producción local vs en Django?

¿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.

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;)

Dos 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.

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 lo que sea de common.py e invalida lo que sea necesario 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:

  1. Todo está en su sistema de versiones, excepto los secretos.
  2. La mayoría de la configuración está en un solo lugar: common.py .
  3. Las cosas específicas de Prod van en prod.py , las cosas específicas de dev.py van en dev.py Es sencillo.
  4. Puede anular cosas de common.py en prod.py o dev.py , y puede anular cualquier cosa en __init__.py .
  5. Es un python sencillo. No hay re-importación de hacks.

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 ser modificado 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).

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!

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 perezosas, valores predeterminados y muchas otras funciones. 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.

Cargar 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 , luego net.domain.ini , luego net.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.
  • Compruebe si hay un “nombre de clúster” declarado y, en ese caso, cargue cluster.cluster_name.ini , que puede definir cosas como las bases de datos y las IP de caché

Como ejemplo de algo que puede lograr con esto, puede definir un valor de “subdominio” por env, que luego se utiliza 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 de esto, tuve 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. .

TL; DR: el truco consiste en modificar os.environment antes de importar la settings/base.py en cualquier settings/.py , esto simplificará enormemente las cosas.


El solo pensar en todos estos archivos entrelazados me da un dolor de cabeza. Combinación, importación (a veces condicional), anulación, parcheo de lo que ya se estableció en caso de que la configuración 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 todos estos en un orden correcto!

Espero finalmente alcanzar el (mi) punto dulce con la solución a continuación.

Recapitulemos los objectives (algunos comunes, algunos míos)

  1. Mantenga los secretos en secreto, no los guarde en un repository.

  2. Configure / lea claves y secretos a través de la configuración del entorno, estilo de 12 factores .

  3. Tener valores predeterminados de reserva razonables. Idealmente para el desarrollo local, no necesita nada más que los valores predeterminados.

  4. … pero trata de mantener segura la producción por defecto. 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.

  5. 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).

  6. Cambiar entre configuraciones de propósito, como local / testing / staging / production, debe basarse solo en DJANGO_SETTINGS_MODULE , nada más.

  7. … pero permite una mayor parametrización a través de la configuración del entorno como DATABASE_URL .

  8. … 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.

  9. 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 .

  10. Responda al DJANGO_SETTINGS_MODULE predeterminado establecido en manage.py durante django-admin startproject

  11. Mantenga 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

  1. 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.

  2. 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 la setup.py test ), hágalo en las herramientas justo antes de ejecutar el código del proyecto.

  3. Evite la magia y el parcheo de cómo django lee sus configuraciones, preprocesar las configuraciones pero no interferir después.

  4. 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/.py mínimas y cortas settings/.py archivos settings/.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 los 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 haya settings/base.py 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 de Django, en su mayoría de vainilla, con algunas condicionales y muchas lecturas del 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:

  1. settings / .py - establece los valores predeterminados según el propósito, no almacena secretos
  2. settings / base.py - es mayormente controlado por el entorno
  3. Configuración del entorno de proceso - ¡12 factores, bebé!
  4. settings / .env - valores predeterminados locales para un inicio fácil

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.

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.

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 llamado local_settings.py que tiene el contenido USING_LOCAL = True in dev y USING_LOCAL = False in prod
  • En settings.py hago una importación en ese archivo para obtener la configuración de USING_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:

  1. Crear settings_base.py donde almaceno configuraciones que son comunes para todos los entornos
  2. 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 --settings=settings_you_wish_to_use.py : manage.py --settings=settings_you_wish_to_use.py )

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.

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. Usaría las variables del entorno para configurar las cosas.

http://django-classy-settings.readthedocs.io/en/latest/

Para utilizar una settings configuración diferente en un entorno diferente, cree un archivo de configuración diferente. And in your deployment script, start the server using --settings= parameter, via which you can use different settings on different environment.

Benefits of using this approach :

  1. Your settings will be modular based on each environment

  2. You may import the master_settings.py containing the base configuration in the environmnet_configuration.py and override the values that you want to change in that environment.

  3. If you have huge team, each developer may have their own local_settings.py which they can add to the code repository without any risk of modifying the server configuration. You can add these local settings to .gitnore if you use git or .hginore if you Mercurial for Version Control (or any other). That way local settings won’t even be the part of actual code base keeping it clean.

I differentiate it in manage.py and created two separate settings file: local_settings.py and prod_settings.py.

In manage.py I check whether the server is local server or production server. If it is a local server it would load up local_settings.py and it is a production server it would load up prod_settings.py. Basically this is how it would look like:

 #!/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) 

I found it to be easier to separate the settings file into two separate file instead of doing lots of ifs inside the settings file.

As an alternative to maintain different file if you wiil: If you are using git or any other VCS to push codes from local to server, what you can do is add the settings file to .gitignore.

This will allow you to have different content in both places without any problem. SO on server you can configure an independent version of settings.py and any changes made on the local wont reflect on server and vice versa.

In addition, it will remove the settings.py file from github also, the big fault, which i have seen many newbies doing.

I had my settings split as follows

 settings/ | |- base.py |- dev.py |- prod.py 

We have 3 environments

  • dev
  • staging
  • production

Now obviously staging and production should have the maximum possible similar environment. So we kept prod.py for both.

But there was a case where I had to identify running server is a production server. @T. Stone ‘s answer helped me write check as follows.

 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 

1 – Create a new folder inside your app and name settings to it.

2 – Now create a new init .py file in it and inside it write

  from .base import * try: from .local import * except: pass try: from .production import * except: pass 

3 – Create three new files in the settings folder name local.py and production.py and base.py

4 – Inside base.py copy all the content of previous settings.p folder and rename it with something different let say old_settings.py

5 – In base.py change your BASE_DIR path to point to your new path of setting

Old path-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath( file )))

New path -> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath( file )))

now in this way the project dir can be structured and can be manageable among production and local development.

Making multiple versions of settings.py is an anti pattern for 12 Factor App methodology . use python-decouple or django-environ instead.

I think the best solution is suggested by @T. Stone, but I don’t know why just don’t use the DEBUG flag in Django. I Write the below code for my website:

 if DEBUG: from .local_settings import * 

Always the simple solutions are better than complex ones.

I found the responses here very helpful. (Has this been more definitively solved? The last response was a year ago.) After considering all the approaches listed, I came up with a solution that I didn’t see listed here.

My criteria were:

  • Everything should be in source control. I don’t like fiddly bits lying around.
  • Ideally, keep settings in one file. I forget things if I’m not looking right at them 🙂
  • No manual edits to deploy. Should be able to test/push/deploy with a single fabric command.
  • Avoid leaking development settings into production.
  • Keep as close as possible to “standard” (*cough*) Django layout as possible.

I thought switching on the host machine made some sense, but then figured the real issue here is different settings for different environments , and had an aha moment. I put this code at the end of my settings.py file:

 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' 

This way, the app defaults to production settings, which means you are explicitly “whitelisting” your development environment. It is much safer to forget to set the environment variable locally than if it were the other way around and you forgot to set something in production and let some dev settings be used.

When developing locally, either from the shell or in a .bash_profile or wherever:

 $ export DJANGO_DEVELOPMENT_SERVER=yep 

(Or if you’re developing on Windows, set via the Control Panel or whatever its called these days… Windows always made it so obscure that you could set environment variables.)

With this approach, the dev settings are all in one (standard) place, and simply override the production ones where needed. Any mucking around with development settings should be completely safe to commit to source control with no impact on production.