Django: ¿Cómo administrar la configuración de desarrollo y producción?

129

He estado desarrollando una aplicación básica. Ahora, en la etapa de implementación, ha quedado claro que necesito tanto una configuración local como una configuración de producción.

Sería genial saber lo siguiente:

  • La mejor manera de lidiar con los entornos de desarrollo y producción.
  • Cómo mantener aplicaciones como django-debug-toolbar solo en un entorno de desarrollo.
  • Otros consejos y prácticas recomendadas para la configuración de desarrollo e implementación.
Kristian Roebuck
fuente

Respuestas:

109

La DJANGO_SETTINGS_MODULEvariable de entorno controla qué archivo de configuración cargará Django.

Por lo tanto, crea archivos de configuración separados para sus respectivos entornos (tenga en cuenta que, por supuesto, pueden hacerlo import *desde un archivo de "configuración compartida" separado) y utilizarlos DJANGO_SETTINGS_MODULEpara controlar cuál usar.

Así es cómo:

Como se indica en la documentación de Django:

El valor de DJANGO_SETTINGS_MODULE debe estar en la sintaxis de ruta de Python, por ejemplo, mysite.settings. Tenga en cuenta que el módulo de configuración debe estar en la ruta de búsqueda de importación de Python.

Entonces, supongamos que lo creó myapp/production_settings.pyy myapp/test_settings.pyen su repositorio de origen.

En ese caso, debería configurar respectivamente el DJANGO_SETTINGS_MODULE=myapp.production_settingsuso del primero y DJANGO_SETTINGS_MODULE=myapp.test_settingsel segundo.


De aquí en adelante, el problema se reduce a configurar la DJANGO_SETTINGS_MODULEvariable de entorno.

Configuración DJANGO_SETTINGS_MODULEmediante un script o un shell

A continuación, puede utilizar un script de arranque o un gestor de procesos para cargar los ajustes correctos (ajustando el medio ambiente), o simplemente ejecutarlo desde su shell antes de comenzar Django: export DJANGO_SETTINGS_MODULE=myapp.production_settings.

Tenga en cuenta que puede ejecutar esta exportación en cualquier momento desde un shell; no es necesario que viva en su .bashrcni en nada.

Configuración DJANGO_SETTINGS_MODULEmediante un administrador de procesos

Si no le gusta escribir un script de arranque que establezca el entorno (¡y hay muy buenas razones para sentirse así!), Le recomendaría usar un administrador de procesos:


Finalmente, tenga en cuenta que puede aprovechar la PYTHONPATHvariable para almacenar la configuración en una ubicación completamente diferente (por ejemplo, en un servidor de producción, almacenándola /etc/). Esto permite separar la configuración de los archivos de la aplicación. Puede que quieras o no eso, depende de cómo esté estructurada tu aplicación.

Thomas Orozco
fuente
7
Para aclarar, dado que el settings.pyarchivo se almacena SiteName/settings.pyde forma predeterminada, si coloca sus archivos de configuración alternativos en el mismo directorio, la línea agregada a bin / enable debería leer DJANGO_SETTINGS_MODULE="SiteName.test_settings"De lo contrario, ¡excelente respuesta!
alexbhandari
2
por coincidencia conoces un tutorial sobre cómo hacer esto paso a paso, soy nuevo en Django y no sé dónde configurar el DJANGO_SETTINGS_MODULE o PYTHONPATH
Jesús Almaral - Hackaprende
Esta solución no parece ser válida para un env conda. No hay bin / enable en un env conda.
Pouya Yousefi
1
@PouyaYousefi: absolutamente no necesitas usar virtualenv para usar esta respuesta. La respuesta realmente se reduce a dos pasos: a) use archivos de configuración separados yb) use DJANGO_SETTINGS_MODULEpara elegir el que desea usar. Modificar bin/activate es uno para hacer lo último (TBH, ya no creo que esto sea una buena idea de todos modos, así que lo eliminé), pero no es el único.
Thomas Orozco
También es útil si está utilizando Django en la edición de la comunidad pycharm y necesita ejecutar pruebas unitarias tanto en la línea de comandos como en la comunidad pycharm correctamente. Suponga que creó solo un archivo de configuración simple en myapp / settings.py en su repositorio de origen. En ese caso, establecería “DJANGO_SETTINGS_MODULE = myapp.settings” en el menú EJECUTAR / Editar configuración / Variable de entorno para usarlo en último lugar para ejecutar casos de prueba.
F.Tamy
58

De forma predeterminada, utilice la configuración de producción, pero cree un archivo llamado settings_dev.pyen la misma carpeta que su settings.pyarchivo. Agregue anulaciones allí, como DEBUG=True.

En la computadora que se usará para el desarrollo, agregue esto a su ~/.bashrcarchivo:

export DJANGO_DEVELOPMENT=true

En la parte inferior de su settings.pyarchivo, agregue lo siguiente.

# Override production variables if DJANGO_DEVELOPMENT env variable is set
if os.environ.get('DJANGO_DEVELOPMENT'):
    from settings_dev import *  # or specific overrides

(Tenga en cuenta que la importación *generalmente debe evitarse en Python)

De forma predeterminada, los servidores de producción no anularán nada. ¡Hecho!

En comparación con las otras respuestas, esta es más simple porque no requiere actualización PYTHONPATHo configuración DJANGO_SETTINGS_MODULEque solo le permite trabajar en un proyecto de django a la vez.

cs01
fuente
8
¿cómo no es esta la respuesta correcta? SO es realmente un desastre hoy en día. Ty cs01
codyc4321
if os.environ.get('DJANGO_DEVELOPMENT', 'true')también funciona. Menciono esto solo porque el is not truemétodo anterior no se pudo importar en Python 3.6.
brt
1
@brt esto es una mala idea: siempre usará su DEVconfiguración que filtrará datos privados en un servidor público. Realmente solo desea verificar que la DJANGO_DEVELOPMENTvariable de entorno existe (es decir is not None).
cs01
Gracias por la información, @ cs01. Me di cuenta de que hice algo mal cuando hice explotar mi sitio con una configuración incorrecta cargando, pero no estaba seguro de por qué se settings_dev.pyestaba cargando en el servidor.
brt
2
@ cs01 Iría tan lejos como para asegurarme de que existe y es veraz, simplemente dejando el is not Nonecheque. También os.getenvestá la taquigrafía
Tjorriemorrie
35

Normalmente tengo un archivo de configuración por entorno y un archivo de configuración compartido:

/myproject/
  settings.production.py
  settings.development.py
  shared_settings.py

Cada uno de mis archivos de entorno tiene:

try:
    from shared_settings import *
except ImportError:
    pass

Esto me permite anular la configuración compartida si es necesario (agregando las modificaciones debajo de esa estrofa).

Luego selecciono qué archivos de configuración usar vinculándolos a settings.py:

ln -s settings.development.py settings.py
Daniel Watkins
fuente
2
¿Cómo lidias con la prohibición de pep8 import *? ¿Desactiva ese cheque? Envolví esta importación en un exec()pero luego no puedo tener condicionales en variables que no están definidas en este archivo, ni puedo alterar la INSTALLED_APPSvariable porque está "indefinida"
Mikhail
11
No filtramos nuestros archivos de configuración, porque en realidad no son tanto código como configuración expresada en Python.
Daniel Watkins
17

Así es como lo hago en 6 sencillos pasos:

  1. Cree una carpeta dentro del directorio de su proyecto y asígnele un nombre settings.

    Estructura del proyecto:

    myproject/
           myapp1/
           myapp2/              
           myproject/
                  settings/
  2. Cree cuatro archivos de Python dentro del settingsdirectorio __init__.py, a saber base.py, dev.pyyprod.py

    Archivos de configuración:

    settings/
         __init__.py
         base.py
         prod.py
         dev.py 
  3. Ábrelo __init__.pyy rellénalo con el siguiente contenido:

    init .py:

    from .base import *
    # you need to set "myproject = 'prod'" as an environment variable
    # in your OS (on which your website is hosted)
    if os.environ['myproject'] == 'prod':
       from .prod import *
    else:
       from .dev import *
  4. Ábralo base.pyy rellénelo con todas las configuraciones comunes (que se usarán tanto en producción como en desarrollo), por ejemplo:

    base.py:

    import os
    ...
    INSTALLED_APPS = [...]
    MIDDLEWARE = [...]
    TEMPLATES = [{...}]
    ...
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    MEDIA_ROOT = os.path.join(BASE_DIR, '/path/')
    MEDIA_URL = '/path/'
  5. Abra dev.pye incluya las cosas que son específicas del desarrollo, por ejemplo:

    dev.py:

    DEBUG = True
    ALLOWED_HOSTS = ['localhost']
    ...
  6. Abra prod.pye incluya las cosas que son específicas de la producción, por ejemplo:

    prod.py:

    DEBUG = False
    ALLOWED_HOSTS = ['www.example.com']
    LOGGING = [...]
    ...
Ahtisham
fuente
10

Cree varios settings*.pyarchivos, extrapolando las variables que deben cambiar por entorno. Luego, al final de su settings.pyarchivo maestro :

try:
  from settings_dev import *
except ImportError:
  pass

Mantienes los settings_*archivos separados para cada etapa.

En la parte superior de su settings_dev.pyarchivo, agregue esto:

import sys
globals().update(vars(sys.modules['settings']))

Para importar variables que necesita modificar.

Esta entrada de wiki tiene más ideas sobre cómo dividir su configuración.

Burhan Khalid
fuente
¡Gracias Burham! Al implementar la aplicación, solo tendría que eliminar el archivo settings_dev para ver mi configuración de implementación en acción.
Kristian Roebuck
Sí, o reemplace la importación consettings_prod.py
Burhan Khalid
1
Sin embargo, editar el archivo master settings.py en una implementación significa que chocará con el control de versiones, por lo que no es necesariamente la mejor manera de avanzar. Yo diría que la opción de Thomas Orozco es la mejor: puede configurar DJANGO_SETTINGS_MODULE en su script virtualenv postactivate o en su configuración de gunicorn o mod_wsgi
Steve Jalim
1
Quizás debería mencionarse que nunca agrega al control de origen los archivos específicos de la etapa. Supuse que se entendía que no impondría configuraciones que fueran específicas de una etapa de un proyecto.
Burhan Khalid
Si está usando virtualenv, por lo general, la configuración predeterminada será {{project_name}}. Entonces, 'configuración' no será una clave en sys.modules. Será 'myproject.settings' (o el nombre de su proyecto). Puede usar modname = "%s.settings" % ".".join(__name__.split('.')[:-1])para obtener el nombre completo del módulo y luego globals().update(vars(sys.modules[modname])). Encuentro que funciona muy bien para mí. Por supuesto, renunciar al bit sobre la determinación programática del nombre del módulo en favor de una cadena probablemente también funcionaría en la mayoría de los casos.
Eric
9

Utilizo las increíbles configuraciones de django , y todas las configuraciones se almacenan en mi settings.py:

from configurations import Configuration

class Base(Configuration):
    # all the base settings here...
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    ...

class Develop(Base):
    # development settings here...
    DEBUG = True 
    ...

class Production(Base):
    # production settings here...
    DEBUG = False

Para configurar el proyecto Django, solo seguí los documentos .

Riccardo Leschiutta
fuente
7

Este es el enfoque que usamos:

  • un settingsmódulo para dividir la configuración en varios archivos para facilitar la lectura;
  • un .env.jsonarchivo para almacenar credenciales y parámetros que queremos excluir de nuestro repositorio de git, o que son específicos del entorno;
  • un env.pyarchivo para leer el .env.jsonarchivo

Considerando la siguiente estructura:

...
.env.json           # the file containing all specific credentials and parameters
.gitignore          # the .gitignore file to exclude `.env.json`
project_name/       # project dir (the one which django-admin.py creates)
  accounts/         # project's apps
    __init__.py
    ...
  ...
  env.py            # the file to load credentials
  settings/
    __init__.py     # main settings file
    database.py     # database conf
    storage.py      # storage conf
    ...
venv                # virtualenv
...

Con .env.jsoncomo:

{
    "debug": false,
    "allowed_hosts": ["mydomain.com"],
    "django_secret_key": "my_very_long_secret_key",
    "db_password": "my_db_password",
    "db_name": "my_db_name",
    "db_user": "my_db_user",
    "db_host": "my_db_host",
}

Y project_name/env.py:

<!-- language: lang-python -->
import json
import os


def get_credentials():
    env_file_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    with open(os.path.join(env_file_dir, '.env.json'), 'r') as f:
        creds = json.loads(f.read())
    return creds


credentials = get_credentials()

Podemos tener las siguientes configuraciones:

<!-- language: lang-py -->
# project_name/settings/__init__.py
from project_name.env import credentials
from project_name.settings.database import *
from project_name.settings.storage import *
...

SECRET_KEY = credentials.get('django_secret_key')

DEBUG = credentials.get('debug')

ALLOWED_HOSTS = credentials.get('allowed_hosts', [])

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    ...
]

if DEBUG:
    INSTALLED_APPS += ['debug_toolbar']

...

# project_name/settings/database.py
from project_name.env import credentials

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': credentials.get('db_name', ''),
        'USER': credentials.get('db_user', ''),
        'HOST': credentials.get('db_host', ''),
        'PASSWORD': credentials.get('db_password', ''),
        'PORT': '5432',
    }
}

los beneficios de esta solución son:

  • credenciales y configuraciones específicas del usuario para el desarrollo local sin modificar el repositorio de git;
  • configuración específica del entorno , puede tener, por ejemplo, tres entornos diferentes con tres diferentes .env.jsoncomo desarrollo, puesta en marcha y producción;
  • las credenciales no están en el repositorio

Espero que esto ayude, solo avíseme si ve alguna advertencia con esta solución.

Charlesthk
fuente
asumiendo dónde envse debe reemplazar dev, prodetc.? ¿Qué va en el settings.pyarchivo antiguo ? ¿Qué hay storage.pyy database.py?
dbinott
Hola @dbinott, puede actualizar fácilmente el env.pyarchivo para que pueda elegir, con una variable de entorno, qué archivo cargar
Charlesthk
Por ejemplo: conf = os.environ.get ('CONF', '') file_ = f ".env. {Conf} .json"
Charlesthk
¿Por qué json en lugar de un tipo de datos Python nativo?
ataque aéreo
4

Yo uso la siguiente estructura de archivos:

project/
   ...
   settings/
   settings/common.py
   settings/local.py
   settings/prod.py
   settings/__init__.py -> local.py

Por __init__.pylo tanto, hay un enlace (en unix o mklink en Windows) ao local.pypuede ser para prod.pyque la configuración aún en el project.settingsmódulo esté limpia y organizada, y si desea usar una configuración en particular, puede usar la variable de entorno DJANGO_SETTINGS_MODULEpara project.settings.prodsi lo necesita para ejecutar un comando para el entorno de producción.

En los archivos prod.pyy local.py:

from .shared import *

DATABASE = {
    ...
}

y el shared.pyarchivo se mantiene global sin configuraciones específicas.

Felipe Buccioni
fuente
3

basándose en la respuesta de cs01:

si tiene problemas con la variable de entorno, establezca su valor en una cadena (por ejemplo, lo hice DJANGO_DEVELOPMENT="true").

También cambié el flujo de trabajo del archivo cs01 de la siguiente manera:

#settings.py
import os
if os.environ.get('DJANGO_DEVELOPMENT') is not None:
    from settings_dev import * 
else:
    from settings_production import *
#settings_dev.py
development settings go here
#settings_production.py
production settings go here

De esta manera, Django no tiene que leer la totalidad de un archivo de configuración antes de ejecutar el archivo de configuración apropiado. Esta solución es útil si su archivo de producción necesita cosas que solo están en su servidor de producción.

Nota: en Python 3, los archivos importados deben tener un .anexo (p from .settings_dev import *. Ej. )

Brian Lee
fuente
1

Si desea mantener 1 archivo de configuración y su sistema operativo de desarrollo es diferente a su sistema operativo de producción, puede ponerlo en la parte inferior de su settings.py:

from sys import platform
if platform == "linux" or platform == "linux2":
    # linux
    # some special setting here for when I'm on my prod server
elif platform == "darwin":
    # OS X
    # some special setting here for when I'm developing on my mac
elif platform == "win32":
    # Windows...
    # some special setting here for when I'm developing on my pc

Leer más: ¿Cómo verifico el sistema operativo en Python?

Usuario
fuente
1

Esto parece haber sido respondido, sin embargo, un método que utilizo combinado con el control de versiones es el siguiente:

Configure un archivo env.py en el mismo directorio que la configuración en mi entorno de desarrollo local que también agrego a .gitignore:

env.py:

#!usr/bin/python

DJANGO_ENV = True
ALLOWED_HOSTS = ['127.0.0.1', 'dev.mywebsite.com']

.gitignore:

mywebsite/env.py

settings.py:

if os.path.exists(os.getcwd() + '/env.py'):
    #env.py is excluded using the .gitignore file - when moving to production we can automatically set debug mode to off:
    from env import *
else:
    DJANGO_ENV = False

DEBUG = DJANGO_ENV

Me parece que esto funciona y es mucho más elegante: con env.py es fácil ver nuestras variables de entorno local y podemos manejar todo esto sin múltiples archivos settings.py o similares. Este método permite utilizar todo tipo de variables de entorno local que no quisiéramos configurar en nuestro servidor de producción. Utilizando .gitignore a través del control de versiones, también mantenemos todo perfectamente integrado.


fuente
Solución más sencilla. También se puede definir todo en una Configclase dentro del env.pyarchivo. Entonces, en lugar de un import *, el módulo puede ser importado por from env import Config. De esta manera, tampoco es necesario usar ese if os.pathcheck, lo que hace que todo esto sea mucho más simple.
Siddharth Pant
0

Utilizar settings.pypara producción. En el mismo directorio, cree settings_dev.pypara anulaciones.

# settings_dev.py

from .settings import * 

DEBUG = False

En una máquina de desarrollo, ejecute su aplicación Django con:

DJANGO_SETTINGS_MODULE=<your_app_name>.settings_dev python3 manage.py runserver

En una máquina de producción, ejecute como si lo acabara de tener settings.pyy nada más.

VENTAJAS

  1. settings.py (utilizado para la producción) es completamente independiente del hecho de que exista cualquier otro entorno.
  2. Para ver la diferencia entre prod y dev que acaba de ver en un solo lugar - settings_dev.py. No hay necesidad de recopilar configuraciones dispersa a través de settings_prod.py, settings_dev.pyy settings_shared.py.
  3. Si alguien agrega una configuración a su configuración de prod después de solucionar un problema de producción, puede estar seguro de que también aparecerá en su configuración de desarrollo (a menos que se anule explícitamente). Por lo tanto, se minimizará la divergencia entre los diferentes archivos de configuración.
Alex Yursha
fuente
0

Para el problema de configurar archivos, elijo copiar

Project
   |---__init__.py   [ write code to copy setting file from subdir to current dir]
   |---settings.py  (do not commit this file to git)
   |---setting1_dir
   |         |--  settings.py
   |---setting2_dir
   |         |--  settings.py

Cuando ejecute django, se ejecutará __init__py. En este momento, settings.py in setting1_dirreemplazará settings.py in Project.

¿Cómo elegir diferentes env?

  • modificar __init__.pydirectamente.
  • hacer un archivo bash para modificar __init__.py.
  • modificar env en linux, y luego dejar __init__.pyleer esta variable.

¿Por qué utilizar de esta manera?

Debido a que no me gustan tantos archivos en el mismo directorio, demasiados archivos confundirán a otros socios y no muy bien para el IDE (el IDE no puede encontrar el archivo que usamos).

Si no desea ver todos estos detalles, puede dividir el proyecto en dos partes.

  1. haga su pequeña herramienta como Spring Initializr, solo para configurar su proyecto. (haga algo como copiar el archivo)
  2. tu código de proyecto
kyakya
fuente
0

Estoy usando un archivo app.yaml diferente para cambiar la configuración entre entornos en el motor de aplicaciones en la nube de Google.

Puede usar esto para crear una conexión proxy en su comando de terminal:

./cloud_sql_proxy -instances=<INSTANCE_CONNECTION_NAME>=tcp:1433

https://cloud.google.com/sql/docs/sqlserver/connect-admin-proxy#macos-64-bit

Archivo: app.yaml

# [START django_app]
service: development
runtime: python37

env_variables:
  DJANGO_DB_HOST: '/cloudsql/myproject:myregion:myinstance'
  DJANGO_DEBUG: True

handlers:
# This configures Google App Engine to serve the files in the app's static
# directory.
- url: /static
  static_dir: static/

# This handler routes all requests not caught above to your main app. It is
# required when static routes are defined, but can be omitted (along with
# the entire handlers section) when there are no static files defined.
- url: /.*
  script: auto
# [END django_app]
Rodrigo Grossi
fuente
-1

Esta es mi solución, con diferentes entornos para desarrollo, prueba y producción.

import socket

[...]

DEV_PC = 'PC059'
host_name = socket.gethostname()

if host_name == DEV_PC:
   #do something
   pass
elif [...]
Massimo Variolo
fuente