Django: Comment gérer les paramètres de développement et de production?

129

J'ai développé une application de base. Maintenant, au stade du déploiement, il est devenu clair que j'ai besoin à la fois de paramètres locaux et de paramètres de production.

Ce serait formidable de savoir ce qui suit:

  • Comment gérer au mieux les paramètres de développement et de production.
  • Comment conserver des applications telles que django-debug-toolbar uniquement dans un environnement de développement.
  • Tous les autres conseils et bonnes pratiques pour les paramètres de développement et de déploiement.
Kristian Roebuck
la source

Réponses:

109

La DJANGO_SETTINGS_MODULEvariable d'environnement contrôle le fichier de paramètres que Django chargera.

Vous créez donc des fichiers de configuration séparés pour vos environnements respectifs (notez qu'ils peuvent bien sûr tous les deux à import *partir d'un fichier «de paramètres partagés» séparé), et utilisez DJANGO_SETTINGS_MODULEpour contrôler lequel utiliser.

Voici comment:

Comme indiqué dans la documentation Django:

La valeur de DJANGO_SETTINGS_MODULE doit être dans la syntaxe de chemin Python, par exemple mysite.settings. Notez que le module de paramètres doit être sur le chemin de recherche d'importation Python.

Alors, supposons que vous avez créé myapp/production_settings.pyet myapp/test_settings.pydans votre référentiel source.

Dans ce cas, vous DJANGO_SETTINGS_MODULE=myapp.production_settingsutiliseriez respectivement le premier et DJANGO_SETTINGS_MODULE=myapp.test_settingsle second.


À partir de maintenant, le problème se résume à la définition de la DJANGO_SETTINGS_MODULEvariable d'environnement.

Réglage à l' DJANGO_SETTINGS_MODULEaide d'un script ou d'un shell

Vous pouvez ensuite utiliser un script d'amorçage ou d' un gestionnaire de processus pour charger les paramètres corrects (en définissant l'environnement), ou courir juste de votre shell avant de lancer Django: export DJANGO_SETTINGS_MODULE=myapp.production_settings.

Notez que vous pouvez exécuter cette exportation à tout moment à partir d'un shell - il n'a pas besoin de vivre dans votre .bashrcou quoi que ce soit.

Configuration à l' DJANGO_SETTINGS_MODULEaide d'un gestionnaire de processus

Si vous n'aimez pas écrire un script bootstrap qui définit l'environnement (et il y a de très bonnes raisons de ressentir cela!), Je vous recommande d'utiliser un gestionnaire de processus:


Enfin, notez que vous pouvez profiter de la PYTHONPATHvariable pour stocker les paramètres dans un emplacement complètement différent (par exemple sur un serveur de production, en les stockant /etc/). Cela permet de séparer la configuration des fichiers d'application. Vous pouvez ou non vouloir cela, cela dépend de la structure de votre application.

Thomas Orozco
la source
7
Pour clarifier, puisque le settings.pyfichier est stocké SiteName/settings.pypar défaut, si vous placez vos fichiers de paramètres alternatifs dans le même répertoire, la ligne ajoutée à bin / activate devrait se lire DJANGO_SETTINGS_MODULE="SiteName.test_settings"Sinon, excellente réponse!
alexbhandari
2
par hasard connaissez-vous un tutoriel sur la façon de procéder étape par étape, je suis nouveau sur Django et je ne sais pas où définir le DJANGO_SETTINGS_MODULE ou PYTHONPATH
Jesus Almaral - Hackaprende
Cette solution ne semble pas être vraie pour un conda env. Il n'y a pas de bin / activate dans un conda env.
Pouya Yousefi
1
@PouyaYousefi: vous n'avez absolument pas besoin d'utiliser virtualenv pour utiliser cette réponse. La réponse se résume en fait à deux étapes: a) utilisez des fichiers de paramètres séparés et b) utilisez DJANGO_SETTINGS_MODULEpour choisir celui que vous souhaitez utiliser. La modification bin/activate est de faire ce dernier (TBH, je ne pense plus que ce soit une bonne idée de toute façon, alors je l'ai retiré), mais ce n'est pas la seule.
Thomas Orozco
C'est également utile si vous utilisez Django dans l'édition de la communauté pycharm et que vous devez exécuter correctement des tests unitaires sur la ligne de commande et la communauté pycharm. Supposons que vous ayez créé un seul fichier de configuration simple dans myapp / settings.py dans votre référentiel source. Dans ce cas, vous définiriez «DJANGO_SETTINGS_MODULE = myapp.settings» dans le menu RUN / Edit Configuration / Environment variable pour l'utiliser plus tard pour exécuter des cas de test.
F.Tamy
58

Par défaut, utilisez les paramètres de production, mais créez un fichier appelé settings_dev.pydans le même dossier que votre settings.pyfichier. Ajoutez-y des remplacements, tels que DEBUG=True.

Sur l'ordinateur qui sera utilisé pour le développement, ajoutez ceci à votre ~/.bashrcfichier:

export DJANGO_DEVELOPMENT=true

Au bas de votre settings.pyfichier, ajoutez ce qui suit.

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

(Notez que l'importation * doit généralement être évitée en Python)

Par défaut, les serveurs de production ne remplaceront rien. Terminé!

Par rapport aux autres réponses, celle-ci est plus simple car elle ne nécessite pas de mise à jour PYTHONPATH, ni de paramétrage DJANGO_SETTINGS_MODULEqui ne vous permet de travailler que sur un seul projet django à la fois.

cs01
la source
8
comment n'est-ce pas la bonne réponse? SO est vraiment un désordre de nos jours. Ty CS01
Codyc4321
if os.environ.get('DJANGO_DEVELOPMENT', 'true')fonctionne également. Je mentionne cela uniquement parce que la is not trueméthode ci-dessus n'a pas pu être importée pour moi sur Python 3.6.
brt
1
@brt c'est une mauvaise idée: il utilisera toujours vos DEVparamètres qui feront fuir des données privées sur un serveur public. Vous voulez vraiment juste vérifier que la DJANGO_DEVELOPMENTvariable d'environnement existe (ie is not None).
cs01
Merci pour l'info, @ cs01. J'ai réalisé que j'avais fait quelque chose de mal lorsque j'ai fait exploser mon site avec un chargement de paramètres incorrects, mais je ne savais pas pourquoi le settings_dev.pychargement sur le serveur.
brt
2
@ cs01 J'irais jusqu'à m'assurer qu'elle existe et qu'elle est véridique, en déposant simplement le is not Nonechèque. Est aussi os.getenvla sténographie
Tjorriemorrie
35

J'ai généralement un fichier de paramètres par environnement et un fichier de paramètres partagé:

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

Chacun de mes fichiers d'environnement a:

try:
    from shared_settings import *
except ImportError:
    pass

Cela me permet de remplacer les paramètres partagés si nécessaire (en ajoutant les modifications ci-dessous cette strophe).

Je sélectionne ensuite les fichiers de paramètres à utiliser en le liant à settings.py:

ln -s settings.development.py settings.py
Daniel Watkins
la source
2
Comment gérez-vous l'interdiction de pep8 import *? Désactivez-vous cette vérification? J'ai enveloppé cette importation dans un, exec()mais je ne peux pas avoir de conditions sur des variables qui ne sont pas définies dans ce fichier, ni modifier la INSTALLED_APPSvariable car elle est "indéfinie"
Mikhail
11
Nous ne peluchons pas nos fichiers de paramètres, car ils ne sont pas vraiment du code mais plutôt une configuration exprimée en Python.
Daniel Watkins
17

Voici comment je le fais en 6 étapes faciles:

  1. Créez un dossier dans le répertoire de votre projet et nommez-le settings.

    Structure du projet:

    myproject/
           myapp1/
           myapp2/              
           myproject/
                  settings/
  2. Créez quatre fichiers python intérieur du settingsrépertoire à savoir __init__.py, base.py, dev.pyetprod.py

    Fichiers de paramètres:

    settings/
         __init__.py
         base.py
         prod.py
         dev.py 
  3. Ouvrez __init__.pyet remplissez-le avec le contenu suivant:

    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. Ouvrez-le base.pyet remplissez-le avec tous les paramètres communs (qui seront utilisés à la fois en production et en développement.) Par exemple:

    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. Ouvrez dev.pyet incluez ce qui est spécifique au développement, par exemple:

    dev.py:

    DEBUG = True
    ALLOWED_HOSTS = ['localhost']
    ...
  6. Ouvrez prod.pyet incluez ce qui est spécifique à la production, par exemple:

    prod.py:

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

Créez plusieurs settings*.pyfichiers en extrapolant les variables qui doivent changer par environnement. Puis à la fin de votre settings.pyfichier maître :

try:
  from settings_dev import *
except ImportError:
  pass

Vous conservez les settings_*fichiers séparés pour chaque étape.

En haut de votre settings_dev.pyfichier, ajoutez ceci:

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

Pour importer des variables que vous devez modifier.

Cette entrée wiki a plus d'idées sur la façon de diviser vos paramètres.

Burhan Khalid
la source
Merci Burham! Lors du déploiement de l'application, il me suffirait de supprimer le fichier settings_dev pour voir mes paramètres de déploiement en action?
Kristian Roebuck
Oui, ou remplacez l'importation parsettings_prod.py
Burhan Khalid
1
La modification du fichier principal settings.py sur un déploiement signifie cependant qu'il va entrer en conflit avec le contrôle de version, ce n'est donc pas nécessairement la meilleure solution. Je dirais que l'option de Thomas Orozco est la meilleure - vous pouvez définir le DJANGO_SETTINGS_MODULE dans votre script postactivation virtualenv ou dans votre configuration gunicorn ou mod_wsgi
Steve Jalim
1
Peut-être faut-il mentionner que vous n'ajoutez jamais au contrôle de source les fichiers spécifiques à l'étape. J'ai supposé qu'il était entendu que vous n'introduisiez pas de paramètres spécifiques à une étape d'un projet.
Burhan Khalid
Si vous utilisez virtualenv, il sera généralement par défaut {{project_name}}. Settings. Ainsi, les «paramètres» ne seront pas une clé dans sys.modules. Ce sera «myproject.settings» (ou quel que soit le nom de votre projet). Vous pouvez utiliser modname = "%s.settings" % ".".join(__name__.split('.')[:-1])pour obtenir le nom complet du module, puis globals().update(vars(sys.modules[modname])). Je trouve que cela fonctionne bien pour moi. Bien sûr, renoncer à la détermination par programme du nom du module en faveur d'une chaîne fonctionnerait probablement également dans la plupart des cas.
Éric
9

J'utilise les super django-configurations , et tous les paramètres sont stockés dans mon 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

Pour configurer le projet Django, j'ai juste suivi la documentation .

Riccardo Leschiutta
la source
7

Voici l'approche que nous utilisons:

  • un settingsmodule pour diviser les paramètres en plusieurs fichiers pour une meilleure lisibilité;
  • un .env.jsonfichier pour stocker les informations d'identification et les paramètres que nous voulons exclure de notre référentiel git, ou qui sont spécifiques à l'environnement;
  • un env.pyfichier pour lire le .env.jsonfichier

Compte tenu de la structure suivante:

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

Avec .env.jsoncomme:

{
    "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",
}

Et 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()

Nous pouvons avoir les paramètres suivants:

<!-- 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',
    }
}

les avantages de cette solution sont:

  • informations d'identification et configurations spécifiques à l'utilisateur pour le développement local sans modifier le référentiel git;
  • configuration spécifique à l'environnement , vous pouvez avoir par exemple trois environnements différents avec trois.env.json comme le développement, la mise en scène et la production;
  • les informations d'identification ne sont pas dans le référentiel

J'espère que cela vous aidera, faites-moi savoir si vous voyez des mises en garde avec cette solution.

Charlesthk
la source
en supposant où envremplacer par dev, prodetc.? Que contient l'ancien settings.pyfichier? Qu'est-ce qu'il y a storage.pyet database.py?
dbinott
Bonjour @dbinott, vous pouvez facilement mettre à jour le env.pyfichier afin de pouvoir choisir, avec une variable d'environnement, quel fichier charger
Charlesthk
Par exemple: conf = os.environ.get ('CONF', '') file_ = f ".env. {Conf} .json"
Charlesthk
Pourquoi voudriez-vous json par opposition à un type de données python natif?
frappe aérienne du
4

J'utilise la structure de fichier suivante:

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

Donc , __init__.pyest un lien (ln dans unix ou mklink dans les fenêtres) à local.pyou peut être à prod.pysi la configuration est encore dans le project.settingsmodule est propre et bien organisé, et si vous souhaitez utiliser une configuration particulière , vous pouvez utiliser la variable d'environnement DJANGO_SETTINGS_MODULEà project.settings.prodsi vous avez besoin pour exécuter une commande pour l'environnement de production.

Dans les fichiers prod.pyet local.py:

from .shared import *

DATABASE = {
    ...
}

et le shared.pyfichier reste global sans configs spécifiques.

Felipe Buccioni
la source
3

en s'appuyant sur la réponse de cs01:

si vous rencontrez des problèmes avec la variable d'environnement, définissez sa valeur sur une chaîne (par exemple, je l'ai fait DJANGO_DEVELOPMENT="true").

J'ai également modifié le flux de travail des fichiers de cs01 comme suit:

#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 cette façon, Django n'a pas besoin de lire l'intégralité d'un fichier de paramètres avant d'exécuter le fichier de paramètres approprié. Cette solution est pratique si votre fichier de production a besoin d'éléments qui se trouvent uniquement sur votre serveur de production.

Remarque: dans Python 3, les fichiers importés doivent avoir un .ajout (par exemple from .settings_dev import *)

Brian Lee
la source
1

Si vous souhaitez conserver 1 fichier de paramètres et que votre système d'exploitation de développement est différent de votre système d'exploitation de production, vous pouvez le placer en bas de votre 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

En savoir plus: Comment vérifier le système d'exploitation en Python?

Utilisateur
la source
1

Cela semble avoir été répondu, mais une méthode que j'utilise combinée avec le contrôle de version est la suivante:

Configurez un fichier env.py dans le même répertoire que les paramètres de mon environnement de développement local que j'ajoute également à .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

Je trouve que cela fonctionne et est beaucoup plus élégant - avec env.py, il est facile de voir nos variables d'environnement local et nous pouvons gérer tout cela sans plusieurs fichiers settings.py ou autres. Cette méthode permet d'utiliser toutes sortes de variables d'environnement locales que nous ne voudrions pas définir sur notre serveur de production. En utilisant le .gitignore via le contrôle de version, nous gardons également tout intégré de manière transparente.


la source
La solution la plus simple. On peut également tout définir dans une Configclasse à l'intérieur d'un env.pyfichier. Ensuite, au lieu d'un import *, le module peut être importé par from env import Config. De cette façon, vous n'avez pas non plus besoin de l'utiliser si os.pathvous vérifiez ce qui rend tout cela beaucoup plus simple.
Siddharth Pant
0

Utiliser settings.pypour la production. Dans le même répertoire, créez settings_dev.pypour les remplacements.

# settings_dev.py

from .settings import * 

DEBUG = False

Sur une machine de développement, exécutez votre application Django avec:

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

Sur une machine prod, exécutez comme si vous venez de l'avoir settings.pyet rien d'autre.

AVANTAGES

  1. settings.py (utilisé pour la production) est totalement indépendant du fait que d'autres environnements existent même.
  2. Pour voir la différence entre prod et dev, il vous suffit de regarder dans un seul emplacement - settings_dev.py. Pas besoin de rassembler des configurations dispersées à travers settings_prod.py, settings_dev.pyet settings_shared.py.
  3. Si quelqu'un ajoute un paramètre à votre configuration de production après avoir résolu un problème de production, vous pouvez être assuré qu'il apparaîtra également dans votre configuration de développement (sauf s'il est explicitement remplacé). Ainsi, la divergence entre les différents fichiers de configuration sera minimisée.
Alex Yursha
la source
0

Pour le problème de paramétrage des fichiers, je choisis de copier

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

Lorsque vous exécutez django, __init__py sera exécuté. En ce moment ,settings.py in setting1_dir remplacera settings.py in Project.

Comment choisir différents env?

  • modifier __init__.py directement.
  • créer un fichier bash à modifier __init__.py .
  • modifiez env sous linux, puis laissez __init__.pylire cette variable.

Pourquoi utiliser de cette façon?

Parce que je n'aime pas autant de fichiers dans le même répertoire, trop de fichiers confondront les autres partenaires et pas très bien pour l'IDE. (IDE ne peut pas trouver quel fichier nous utilisons)

Si vous ne voulez pas voir tous ces détails, vous pouvez diviser le projet en deux parties.

  1. faites votre petit outil comme Spring Initializr, juste pour configurer votre projet (faites qc comme copier un fichier)
  2. votre code de projet
kyakya
la source
0

J'utilise un fichier app.yaml différent pour modifier la configuration entre les environnements dans le moteur d'application Google Cloud.

Vous pouvez l'utiliser pour créer une connexion proxy dans votre commande de terminal:

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

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

Fichier: 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
la source
-1

C'est ma solution, avec différents environnements de développement, de test et de production

import socket

[...]

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

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