Comment gérer les paramètres locaux vs de production dans Django?

298

Quelle est la méthode recommandée pour gérer les paramètres du développement local et du serveur de production? Certains d'entre eux (comme les constantes, etc.) peuvent être modifiés / accessibles dans les deux, mais certains d'entre eux (comme les chemins d'accès aux fichiers statiques) doivent rester différents, et ne doivent donc pas être remplacés à chaque fois que le nouveau code est déployé.

Actuellement, j'ajoute toutes les constantes à settings.py. Mais chaque fois que je change une constante localement, je dois la copier sur le serveur de production et éditer le fichier pour des changements spécifiques à la production ... :(

Edit: il semble qu'il n'y ait pas de réponse standard à cette question, j'ai accepté la méthode la plus populaire.

akv
la source
Veuillez consulter les configurations de django .
JJD
2
La méthode acceptée n'est plus la plus populaire.
Daniel
2
django-split-settings est très facile à utiliser. Il ne nécessite pas de réécrire les paramètres par défaut.
sobolevn
vous devez utiliser le fichier base.py et dans votre local.py "from .base import *", le même dans votre production.py "from .base import *", vous devez exécuter votre projet avec: python manage.py runserver - settings =
project_name.settings.local

Réponses:

127

Dans settings.py:

try:
    from local_settings import *
except ImportError as e:
    pass

Vous pouvez remplacer ce qui est nécessaire dans local_settings.py; il devrait alors rester hors de votre contrôle de version. Mais comme vous mentionnez la copie, je suppose que vous n'en utilisez pas;)

ohnoes
la source
3
Pour faciliter le suivi / déploiement des nouveaux paramètres, utilisez un "local_settings.py" sur les machines de production / test et aucun sur le développement.
John Mee
8
C'est comme ça que je le fais - en ajoutant ces lignes à la fin de settings.py pour qu'ils puissent remplacer les paramètres par défaut
daonb
61
Cette approche signifie que vous disposez d'un code non versionné en cours de développement et de production. Et chaque développeur a une base de code différente. J'appelle anti-pattern ici.
pydanny
8
@pydanny Le problème est que Django stocke sa configuration dans un fichier .py. Vous ne pouvez pas vous attendre à ce que tous les développeurs et le serveur de production utilisent les mêmes paramètres, vous devez donc modifier ce fichier .py ou implémenter une solution alternative (fichiers .ini, environnement, etc.).
Tupteq
3
Je préfère appeler le module settings_localplutôt que local_settingsde le regrouper settings.pydans des listes de dossiers alphabétiques. Gardez settings_local.pyhors de contrôle de version en utilisant .gitignorecomme informations d'identification n'appartiennent pas à Git. Imaginez que vous les approvisionniez par accident. Je garde dans git un fichier modèle appelé à la settings_local.py.txtplace.
fmalina
297

Two Scoops of Django: Best Practices for Django 1.5 suggère d'utiliser le contrôle de version pour vos fichiers de paramètres et de stocker les fichiers dans un répertoire séparé:

project/
    app1/
    app2/
    project/
        __init__.py
        settings/
            __init__.py
            base.py
            local.py
            production.py
    manage.py

Le base.pyfichier contient des paramètres communs (tels que MEDIA_ROOT ou ADMIN), local.pyet production.pypossède des paramètres spécifiques au site:

Dans le fichier de base settings/base.py:

INSTALLED_APPS = (
    # common apps...
)

Dans le fichier des paramètres de développement local settings/local.py:

from project.settings.base import *

DEBUG = True
INSTALLED_APPS += (
    'debug_toolbar', # and other apps for local development
)

Dans le fichier de paramètres de production de fichiers settings/production.py:

from project.settings.base import *

DEBUG = False
INSTALLED_APPS += (
    # other apps for production site
)

Ensuite, lorsque vous exécutez django, vous ajoutez l' --settingsoption:

# 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

Les auteurs du livre ont également mis en place un exemple de modèle de mise en page de projet sur Github.

gene_wood
la source
62
Notez qu'au lieu d'utiliser à --settingschaque fois, vous pouvez définir la DJANGO_SETTINGS_MODULEvariable envvar. Cela fonctionne bien avec, par exemple, Heroku: définissez-le globalement en production, puis remplacez-le par dev dans votre fichier .env.
Simon Weber
9
Utiliser DJANGO_SETTINGS_MODULEenv var est la meilleure idée ici, merci Simon.
kibibu
20
Vous devrez peut-être modifier les BASE_DIRparamètres pouros.path.dirname(os.path.realpath(os.path.dirname(__file__) + "/.."))
Petr Peller
5
@rsp selon les django docs, vous importez from django.conf import settingsqui est un objet qui résume l'interface et découple le code de l'emplacement des paramètres, docs.djangoproject.com/en/dev/topics/settings/…
3
Si je définis DJANGO_SETTINGS_MODULE via une variable d'environnement, ai-je toujours besoin d'os.environ.setdefault ("DJANGO_SETTINGS_MODULE", "projectname.settings.production") dans mon fichier wsgi.py? J'ai également défini la variable d'environnement en utilisant: export DJANGO_SETTINGS_MODULE = projectname.settings.local, mais elle est perdue lorsque je ferme le terminal. Que puis-je faire pour m'en assurer? Dois-je ajouter cette ligne au fichier bashrc?
Kritz
71

Au lieu de settings.py, utilisez cette disposition:

.
└── settings/
    ├── __init__.py  <= not versioned
    ├── common.py
    ├── dev.py
    └── prod.py

common.py est l'endroit où vit la majeure partie de votre configuration.

prod.py importe tout de commun et remplace tout ce dont il a besoin pour remplacer:

from __future__ import absolute_import # optional, but I like it
from .common import *

# Production overrides
DEBUG = False
#...

De même, dev.pyimporte tout common.pyet remplace tout ce dont il a besoin pour remplacer.

Enfin, __init__.pyc'est là que vous décidez quels paramètres charger, et c'est aussi où vous stockez les secrets (par conséquent, ce fichier ne doit pas être versionné):

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

Ce que j'aime dans cette solution c'est:

  1. Tout est dans votre système de version, sauf les secrets
  2. La plupart configuration est en un seul endroit: common.py.
  3. Les choses spécifiques au produit entrent prod.py, les choses spécifiques au développeur entrent dev.py. C'est simple.
  4. Vous pouvez remplacer des éléments common.pydans prod.pyou dev.py, et vous pouvez remplacer n'importe quoi dans __init__.py.
  5. C'est du python simple. Pas de piratage de réimportation.
MiniQuark
la source
2
J'essaie toujours de comprendre ce que je dois définir dans mes fichiers project.wsgi et manage.py pour le fichier de paramètres. Allez-vous nous éclairer là-dessus? Plus précisément, dans mon fichier manage.py que j'ai os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings")foobar est un dossier avec un __init__.pyfichier et les paramètres est un dossier avec un __init__.pyfichier qui contient mes secrets et importe dev.py, qui importe ensuite common.py. EDIT Nevermind, je n'avais pas un module installé qui était nécessaire. Ma faute! Cela fonctionne très bien !!
teewuane
5
Deux choses: 1) mieux vaut définir Debug = True dans votre dev.py plutôt que = False dans votre prod.py. 2) Plutôt que de basculer dans init .py, basculez à l'aide de l'environnement DJANGO_SETTINGS_MODULE var. Cela vous aidera avec les déploiements PAAS (par exemple Heroku).
Rob Grant
Lorsque j'utilise cette configuration dans django 1.8.4 et que j'essaye runserver, j'obtiens "django.core.exceptions.ImproperlyConfigured: le paramètre SECRET_KEY ne doit pas être vide.", Même si j'ai SECRET_KEY sur mon fichier init .py. Suis-je en train de manquer quelque chose?
polarcare
l'utilisation de quelque chose comme AWS_SECRET_ACCESS_KEY = os.getenv ("AWS_SECRET_ACCESS_KEY") n'est-elle pas plus sûre? Question honnête - je sais pourquoi vous ne voulez pas qu'il soit versionné, mais l'autre alternative est de l'obtenir de l'environnement. Ce qui pose la question de la définition de la variable d'environnement, bien sûr, mais cela peut être laissé à votre mécanisme de déploiement, non?
JL Peyret
20

J'utilise une version légèrement modifiée du style de paramètres "if DEBUG" publié par Harper Shelby. Évidemment, en fonction de l'environnement (win / linux / etc.), Le code devra peut-être être légèrement modifié.

J'utilisais par le passé "if DEBUG" mais j'ai trouvé que de temps en temps je devais faire des tests avec DEUBG réglé sur False. Ce que je voulais vraiment distinguer si l'environnement était la production ou le développement, ce qui m'a donné la liberté de choisir le niveau 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'

Je considérerais toujours cette méthode de configuration comme un travail en cours. Je n'ai vu aucune façon de gérer les paramètres de Django qui couvrait toutes les bases et en même temps, je n'ai pas été très compliqué à configurer (je ne suis pas en panne avec les méthodes des fichiers de paramètres 5x).

T. Stone
la source
C'est le genre de chose que les paramètres de Django étant un véritable fichier de code permettent, et je faisais allusion. Je n'ai rien fait de tel moi-même, mais c'est certainement le genre de solution qui pourrait être une meilleure réponse générale que la mienne.
Harper Shelby
3
Je viens de rencontrer cela pour la première fois et j'ai choisi (avec succès!) D'utiliser votre solution, avec une légère différence: j'ai utilisé uuid.getnode () pour trouver l'uuid de mon système. Je teste donc si uuid.getnode () == 12345678901 (en fait un nombre différent) au lieu du test os.environ que vous avez utilisé. Je n'ai pas trouvé de documentation pour me convaincre que os.environ ['COMPUTERNAME'] est unique par ordinateur.
Joe Golton
os.environ ['COMPUTERNAME'] ne fonctionne pas sur Amazon AWS Ubuntu. Je reçois une erreur KeyError.
nu everest
Lors de l'utilisation de l'UUID, cette solution s'est avérée être la meilleure et la plus simple pour moi. Il ne nécessite pas beaucoup de patchwork compliqué et trop modulaire. Dans un environnement de production, vous devez toujours placer vos mots de passe de base de données et SECRET_KEY dans un fichier distinct qui réside en dehors du contrôle de version.
nu everest
os.environ['COMPUTERNAME']ne fonctionne malheureusement pas sur PythonAnywhere. Vous obtenez une KeyError.
nbeuchat
14

J'utilise un settings_local.py et un settings_production.py. Après avoir essayé plusieurs options, j'ai constaté qu'il est facile de perdre du temps avec des solutions complexes lorsque le simple fait d'avoir deux fichiers de paramètres semble facile et rapide.

Lorsque vous utilisez mod_python / mod_wsgi pour votre projet Django, vous devez le pointer vers votre fichier de paramètres. Si vous le pointez sur app / settings_local.py sur votre serveur local et app / settings_production.py sur votre serveur de production, la vie devient facile. Modifiez simplement le fichier de paramètres approprié et redémarrez le serveur (le serveur de développement Django redémarrera automatiquement).

Kai
la source
2
Et qu'en est-il du serveur de développement local? existe-t-il un moyen de dire au serveur Web django (exécuté en utilisant python manage.py runserver), quel fichier de paramètres utiliser?
akv
2
@akv si vous ajoutez --settings = [nom du module] (pas d'extension .py) à la fin de la commande runserver, vous pouvez spécifier le fichier de paramètres à utiliser. Si vous allez faire cela, faites-vous plaisir et créez un script shell / fichier batch avec les paramètres de développement configurés. Faites-moi confiance, vos doigts vous remercieront.
T. Stone,
c'est la solution que j'utilise. pirater un fichier de paramètres à utiliser pour la production ou le développement est
compliqué
4
Je pense qu'il vaut mieux utiliser settings.py dans le développement, car vous n'avez pas à le spécifier tout le temps.
Andre Bossard
Ai-je raison de supposer que cette méthode nécessite l'importation du module de paramètres via le proxy, django.conf.settings? Sinon, vous devrez modifier les déclarations d'importation pour pointer vers le fichier de paramètres correct lors de la diffusion en direct.
Groady
8

TL; DR: L'astuce est de modifier os.environmentavant d'importer settings/base.pyn'importe quoi settings/<purpose>.py, cela simplifiera grandement les choses.


Le simple fait de penser à tous ces fichiers entrelacés me donne mal à la tête. Combiner, importer (parfois de manière conditionnelle), remplacer, corriger ce qui était déjà défini au cas où le DEBUGparamètre serait modifié ultérieurement. Quel cauchemard!

Au fil des ans, je suis passé par toutes les différentes solutions. Ils fonctionnent tous un peu , mais sont si pénibles à gérer. WTF! Avons-nous vraiment besoin de tout ce tracas? Nous avons commencé avec un seul settings.pyfichier. Maintenant, nous avons besoin d'une documentation juste pour combiner correctement tous ces éléments dans un ordre correct!

J'espère que j'ai finalement atteint le (mon) sweet spot avec la solution ci-dessous.

Récapitulons les objectifs (certains communs, certains miens)

  1. Gardez les secrets secrets - ne les stockez pas dans un repo!

  2. Définissez / lisez les clés et les secrets via les paramètres d'environnement, style 12 facteurs .

  3. Avoir des valeurs par défaut de secours raisonnables. Idéalement pour le développement local, vous n'avez besoin de rien de plus que les valeurs par défaut.

  4. … Mais essayez de sécuriser la production par défaut. Il vaut mieux manquer un remplacement de paramètre localement, que de se rappeler de régler les paramètres par défaut en toute sécurité pour la production.

  5. Avoir la possibilité de changer DEBUG / désactiver d'une manière qui peut avoir un effet sur d'autres paramètres (par exemple en utilisant javascript compressé ou non).

  6. Le basculement entre les paramètres d'objectif, comme local / test / mise en scène / production, doit être basé uniquement sur DJANGO_SETTINGS_MODULE rien de plus.

  7. … Mais permettre un paramétrage plus poussé via des paramètres d'environnement tels que DATABASE_URL .

  8. … Leur permet également d'utiliser différents paramètres de finalité et de les exécuter localement côte à côte, par exemple. configuration de la production sur la machine du développeur local, pour accéder à la base de données de production ou aux feuilles de style compressées du test de fumée.

  9. Échec si une variable d'environnement n'est pas explicitement définie (nécessitant une valeur vide au minimum), en particulier en production, par exemple. EMAIL_HOST_PASSWORD.

  10. Répondre à l' DJANGO_SETTINGS_MODULEensemble par défaut dans manage.py pendant le démarrage du projet django-admin

  11. Gardez les conditions au minimum, si la condition est le type d'environnement prévu (par exemple, pour le fichier journal de l'ensemble de production et sa rotation), remplacez les paramètres dans le fichier de paramètres proposé.

Ne pas

  1. Ne laissez pas django lire le paramètre DJANGO_SETTINGS_MODULE dans un fichier.
    Pouah! Pensez à quel point c'est méta. Si vous avez besoin d'un fichier (comme docker env), lisez-le dans l'environnement avant de démarrer un processus django.

  2. Ne remplacez pas DJANGO_SETTINGS_MODULE dans votre code de projet / application, par exemple. basé sur le nom d'hôte ou le nom du processus.
    Si vous êtes paresseux pour définir la variable d'environnement (comme poursetup.py test ), faites-le dans l'outillage juste avant d'exécuter votre code de projet.

  3. Évitez la magie et la correction de la façon dont django lit ses paramètres, prétraitez les paramètres mais n'interférez pas par la suite.

  4. Pas de bêtises logiques compliquées. La configuration doit être fixe et matérialisée et non calculée à la volée. Fournir un défaut par défaut est juste assez logique ici.
    Voulez-vous vraiment déboguer, pourquoi localement vous avez un ensemble de paramètres correct mais en production sur un serveur distant, sur une centaine de machines, quelque chose de calculé différemment? Oh! Tests unitaires? Pour les paramètres? Sérieusement?

Solution

Ma stratégie consiste en un excellent environnement django utilisé avec inides fichiers de style, fournissant des os.environmentvaleurs par défaut pour le développement local, quelques settings/<purpose>.pyfichiers minimaux et courts qui ont un import settings/base.py APRÈS leos.environment a été créée à partir d' un INIfichier. Cela nous donne effectivement une sorte d'injection de paramètres.

L'astuce consiste à modifier os.environmentavant d'importersettings/base.py .

Pour voir l'exemple complet, faites le repo: https://github.com/wooyek/django-settings-strategy

.
   manage.py
├───data
└───website
    ├───settings
          __init__.py   <-- imports local for compatibility
          base.py       <-- almost all the settings, reads from proces environment 
          local.py      <-- a few modifications for local development
          production.py <-- ideally is empty 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

settings / .env

A par défaut pour le développement local. Un fichier secret, pour définir principalement les variables d'environnement requises. Définissez-les sur des valeurs vides si elles ne sont pas requises dans le développement local. Nous fournissons des valeurs par défaut ici et ne settings/base.pypas échouer sur une autre machine si elles sont absentes de l'environnement.

settings / local.py

Ce qui se passe ici, c'est charger l'environnement depuis settings/.env, puis importer les paramètres communs depuis settings/base.py. Après cela, nous pouvons en remplacer quelques-uns pour faciliter le développement 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

Pour la production, nous ne devons pas nous attendre à un fichier d'environnement, mais il est plus facile d'en avoir un si nous testons quelque chose. Mais de toute façon, de peur de fournir peu de valeurs par défaut en ligne, vous settings/base.pypouvez donc réagir en conséquence.

environ.Env.read_env(Path(__file__) / "production.env", DEBUG='False', ASSETS_DEBUG='False')
from .base import *

Le principal point d'intérêt ici est DEBUGet ASSETS_DEBUGremplace, ils seront appliqués au pythonos.environ UNIQUEMENT s'ils sont MANQUANTS de l'environnement et du fichier.

Ce seront nos valeurs par défaut de production, pas besoin de les mettre dans l'environnement ou le fichier, mais elles peuvent être remplacées si nécessaire. Soigné!

settings / base.py

Ce sont vos paramètres de django principalement vanille, avec quelques conditions et beaucoup de lecture à partir de l'environnement. Presque tout est là, en gardant tous les environnements prévus cohérents et aussi similaires que possible.

Les principales différences sont ci-dessous (j'espère qu'elles sont explicites):

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',)

Le dernier bit montre la puissance ici. ASSETS_DEBUGa une valeur par défaut raisonnable, qui peut être remplacée settings/production.pyet même celle qui peut être remplacée par un paramètre d'environnement! Yay!

En effet, nous avons une hiérarchie mixte d'importance:

  1. settings / .py - définit les valeurs par défaut en fonction de l'objectif, ne stocke pas les secrets
  2. settings / base.py - est principalement contrôlé par l'environnement
  3. paramètres d'environnement de processus - 12 facteurs bébé!
  4. settings / .env - valeurs par défaut locales pour un démarrage facile
Janusz Skonieczny
la source
Hey Janusz ... donc dans le fichier .env iraient toutes les clés API et les clés d'authentification et les mots de passe, etc.? Tout comme TWILLIO_API = "abc123"? Ou TWILLIO_API = env ("TWILLIO_API")?
dbinott
Oui, mais ce n'est qu'une solution de rechange pour les paramètres d'environnement. Ce fichier est très pratique pour le développement, mais n'est pas enregistré dans le référentiel ni poussé en production où vous devez utiliser strictement les paramètres d'environnement ou votre équivalent de plate-forme qui, à son tour, définira les paramètres d'environnement pour le processus du serveur.
Janusz Skonieczny
7

Je gère mes configurations à l'aide de django-split-settings .

Il s'agit d'un remplacement direct pour les paramètres par défaut. C'est simple, mais configurable. Et la refactorisation de vos paramètres existants n'est pas nécessaire.

Voici un petit exemple (fichier 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()
    )

C'est tout.

Mettre à jour

J'ai écrit un article de blog sur la gestion djangodes paramètres avec django-split-sttings. Regarde!

sobolevn
la source
1
J'ai essayé cela .. je suis tombé sur un mur une fois que j'ai essayé d'exécuter mes tests unitaires django .. je n'arrivais pas à comprendre comment spécifier le fichier de paramètres à lire
abbood
J'ai créé un résumé pour vous: gist.github.com/sobolevn/006c734f0520439a4b6c16891d65406c
sobolevn
j'ai quelque chose comme ça dans mon code, donc je vérifie l'indicateur settings.DEBUG pour savoir si je veux importer des trucs .. cet indicateur est toujours défini sur false dans les tests unitaires django (voir ici ) donc mon travail consiste à les remplacer à chaque test comme ça
abbood
voici une autre question cependant: mon uwsgi.inifichier a des paramètres différents dans dev / prod .. une idée de la façon de lui faire choisir des valeurs dans mon fichier de paramètres?
abbood
désolé, je n'ai pas la configuration. vous pouvez poser une question séparée avec plus de détails et je vais essayer de vous aider.
sobolevn
6

Le problème avec la plupart de ces solutions est que vos paramètres locaux sont appliqués avant les plus courants ou après eux.

Il est donc impossible de remplacer des choses comme

  • les paramètres spécifiques à env définissent les adresses du pool memcached, et dans le fichier de paramètres principal, cette valeur est utilisée pour configurer le backend du cache
  • les paramètres spécifiques à env ajoutent ou suppriment des applications / middleware à ceux par défaut

en même temps.

Une solution peut être implémentée en utilisant des fichiers de configuration de style "ini" avec la classe ConfigParser. Il prend en charge plusieurs fichiers, l'interpolation de chaînes paresseuses, les valeurs par défaut et de nombreux autres avantages. Une fois un certain nombre de fichiers chargés, d'autres fichiers peuvent être chargés et leurs valeurs remplaceront les précédentes, le cas échéant.

Vous chargez un ou plusieurs fichiers de configuration, selon l'adresse de la machine, les variables d'environnement et même les valeurs des fichiers de configuration précédemment chargés. Ensuite, vous utilisez simplement les valeurs analysées pour remplir les paramètres.

Une stratégie que j'ai utilisée avec succès a été:

  • Charger une valeur par défaut defaults.ini fichier
  • Vérifiez le nom de la machine et chargez tous les fichiers qui correspondent au FQDN inversé, de la correspondance la plus courte à la correspondance la plus longue (donc, j'ai chargé net.ini, puis net.domain.ini, puisnet.domain.webserver01.ini , chacun remplaçant éventuellement les valeurs de la précédente). Ce compte également pour les machines des développeurs, afin que chacun puisse configurer son pilote de base de données préféré, etc. pour le développement local
  • Vérifiez s'il y a un "nom de cluster" déclaré, et dans ce cas chargez cluster.cluster_name.ini, qui peut définir des choses comme la base de données et les IP de cache

Comme exemple de quelque chose que vous pouvez réaliser avec cela, vous pouvez définir une valeur de "sous-domaine" par env, qui est ensuite utilisée dans les paramètres par défaut (comme hostname: %(subdomain).whatever.net ) pour définir tous les noms d'hôte et les cookies nécessaires que django doit fonctionner.

C'est aussi sec que j'ai pu obtenir, la plupart des fichiers (existants) n'avaient que 3 ou 4 paramètres. En plus de cela, je devais gérer la configuration du client, donc un ensemble supplémentaire de fichiers de configuration (avec des choses comme les noms de base de données, les utilisateurs et les mots de passe, le sous-domaine attribué, etc.) existait, un ou plusieurs par client.

On peut faire évoluer cela aussi bas ou aussi haut que nécessaire, il vous suffit de mettre dans le fichier de configuration les clés que vous souhaitez configurer par environnement, et une fois qu'il y a besoin d'une nouvelle configuration, mettez la valeur précédente dans la configuration par défaut et remplacez-la Où il faut.

Ce système s'est révélé fiable et fonctionne bien avec le contrôle de version. Il est utilisé depuis longtemps pour gérer deux clusters d'applications distincts (15 instances distinctes ou plus du site django par machine), avec plus de 50 clients, où les clusters changeaient de taille et de membres en fonction de l'humeur de l'administrateur système. .

réécrit
la source
1
Avez-vous un exemple de la façon dont vous chargez les paramètres de l'ini dans les paramètres de Django?
kaleissin
Voir docs.python.org/2/library/configparser.html . Vous pouvez charger un analyseur avec config = ConfigParser.ConfigParser() puis lire vos fichiers config.read(array_of_filenames)et obtenir des valeurs en utilisant config.get(section, option). Vous commencez donc par charger votre configuration, puis vous l'utilisez pour lire les valeurs des paramètres.
réécrit
5

Je travaille également avec Laravel et j'aime l'implémentation là-bas. J'ai essayé de l'imiter et de le combiner avec la solution proposée par T. Stone (regardez ci-dessus):

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

Peut-être que quelque chose comme ça pourrait vous aider.

Robert Kuzma
la source
4

N'oubliez pas que settings.py est un fichier de code en direct. En supposant que DEBUG n'est pas défini sur la production (ce qui est une bonne pratique), vous pouvez faire quelque chose comme:

if DEBUG:
    STATIC_PATH = /path/to/dev/files
else:
    STATIC_PATH = /path/to/production/files

Assez basique, mais vous pourriez, en théorie, aller jusqu'à n'importe quel niveau de complexité basé uniquement sur la valeur de DEBUG - ou toute autre variable ou vérification de code que vous vouliez utiliser.

Harper Shelby
la source
4

Pour la plupart de mes projets, j'utilise le modèle suivant:

  1. Créer settings_base.py où je stocke les paramètres communs à tous les environnements
  2. Chaque fois que j'ai besoin d'utiliser un nouvel environnement avec des exigences spécifiques, je crée un nouveau fichier de paramètres (par exemple, settings_local.py) qui hérite du contenu de settings_base.py et remplace / ajoute les variables de paramètres appropriées ( from settings_base import *)

(Pour exécuter manage.py avec des paramètres personnalisés fichier que vous utilisez simplement --settings option de commande: manage.py <command> --settings=settings_you_wish_to_use.py)

dzida
la source
3

Ma solution à ce problème est également un mélange de solutions déjà énoncées ici:

  • Je garde un fichier appelé local_settings.pyqui a le contenu USING_LOCAL = Trueen dev et USING_LOCAL = Falseen prod
  • Dans settings.pyje fais une importation sur ce fichier pour obtenir le USING_LOCALréglage

Je base ensuite tous mes paramètres dépendants de l'environnement sur celui-ci:

DEBUG = USING_LOCAL
if USING_LOCAL:
    # dev database settings
else:
    # prod database settings

Je préfère cela à deux fichiers settings.py distincts que je dois conserver car je peux garder mes paramètres structurés dans un seul fichier plus facilement que de les répartir sur plusieurs fichiers. Ainsi, lorsque je mets à jour un paramètre, je n'oublie pas de le faire pour les deux environnements.

Bien sûr, chaque méthode a ses inconvénients et celle-ci ne fait pas exception. Le problème ici est que je ne peux pas écraser le local_settings.pyfichier chaque fois que je pousse mes modifications en production, ce qui signifie que je ne peux pas simplement copier tous les fichiers à l'aveugle, mais c'est quelque chose avec lequel je peux vivre.

Miguel Ventura
la source
3

J'utilise une variation de ce que jpartogi a mentionné ci-dessus, que je trouve un peu plus courte:

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)

Fondamentalement, sur chaque ordinateur (développement ou production), j'ai le fichier hostname_settings.py approprié qui est chargé dynamiquement.

stratosgear
la source
3

Il existe également des paramètres Django Classy. Personnellement, j'en suis un grand fan. Il est construit par l'une des personnes les plus actives du Django IRC. Vous utiliseriez des variables d'environnement pour définir les choses.

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

SudoKid
la source
3

1 - Créez un nouveau dossier dans votre application et nommez-y les paramètres.

2 - Maintenant, créez un nouveau __init__.pyfichier dedans et écrivez dedans

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 - Créez trois nouveaux fichiers dans le nom du dossier des paramètres local.pyet production.pyet base.py.

4 - À l'intérieur base.py, copiez tout le contenu du settings.pydossier précédent et renommez-le avec quelque chose de différent, disonsold_settings.py .

5 - Dans base.py, changez votre chemin BASE_DIR pour pointer vers votre nouveau chemin de réglage

Ancien chemin-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Nouveau chemin -> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

De cette façon, le répertoire du projet peut être structuré et gérable entre la production et le développement local.

Jack Ryan
la source
2

Afin d'utiliser une settingsconfiguration différente sur un environnement différent, créez un fichier de paramètres différent. Et dans votre script de déploiement, démarrez le serveur en utilisant un --settings=<my-settings.py>paramètre, via lequel vous pouvez utiliser différents paramètres sur un environnement différent.

Avantages de l'utilisation de cette approche :

  1. Vos paramètres seront modulaires en fonction de chaque environnement

  2. Vous pouvez importer le master_settings.pycontenant la configuration de base dans le environmnet_configuration.pyet remplacer les valeurs que vous souhaitez modifier dans cet environnement.

  3. Si vous avez une énorme équipe, chaque développeur peut avoir le sien local_settings.pyqu'il peut ajouter au référentiel de code sans risque de modifier la configuration du serveur. Vous pouvez ajouter ces paramètres locaux .gitnoresi vous utilisez git ou .hginoresi vous Mercurial pour le contrôle de version (ou tout autre). De cette façon, les paramètres locaux ne feront même pas partie de la base de code réelle en le maintenant propre.

Moinuddin Quadri
la source
2

J'ai eu mes paramètres divisés comme suit

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

Nous avons 3 environnements

  • dev
  • mise en scène
  • production

Désormais, la mise en scène et la production devraient avoir le plus grand environnement similaire possible. Nous avons donc gardéprod.py deux.

Mais il y a eu un cas où j'ai dû identifier le serveur en cours d'exécution comme un serveur de production. @T. La réponse de Stone m'a aidé à écrire le chèque comme suit.

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  
Kishor Pawar
la source
1

Je le différencie dans manage.py et j'ai créé deux fichiers de paramètres distincts: local_settings.py et prod_settings.py.

Dans manage.py, je vérifie si le serveur est un serveur local ou un serveur de production. S'il s'agit d'un serveur local, il chargerait local_settings.py et c'est un serveur de production qu'il chargerait prod_settings.py. En gros, voici à quoi cela ressemblerait:

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

J'ai trouvé qu'il était plus facile de séparer le fichier de paramètres en deux fichiers distincts au lieu de faire beaucoup d'if à l'intérieur du fichier de paramètres.

Joshua Partogi
la source
1

Comme alternative pour maintenir un fichier différent si vous voulez: Si vous utilisez git ou tout autre VCS pour pousser les codes du local vers le serveur, ce que vous pouvez faire est d'ajouter le fichier de paramètres au .gitignore.

Cela vous permettra d'avoir un contenu différent aux deux endroits sans aucun problème. SO sur le serveur, vous pouvez configurer une version indépendante de settings.py et toutes les modifications apportées sur le local ne se refléteront pas sur le serveur et vice versa.

De plus, cela supprimera également le fichier settings.py de github, le gros défaut, ce que j'ai vu de nombreux débutants faire.

sprksh
la source
0

Je pense que la meilleure solution est suggérée par @T. Stone, mais je ne sais pas pourquoi n'utilisez pas le drapeau DEBUG dans Django. J'écris le code ci-dessous pour mon site Web:

if DEBUG:
    from .local_settings import *

Les solutions simples sont toujours meilleures que les solutions complexes.

seyedrezafar
la source
-2

J'ai trouvé les réponses ici très utiles. (Cela a-t-il été résolu de manière plus définitive? La dernière réponse remonte à un an.) Après avoir examiné toutes les approches répertoriées, j'ai trouvé une solution que je ne voyais pas répertoriée ici.

Mes critères étaient:

  • Tout devrait être sous contrôle de source. Je n'aime pas les morceaux délicats qui traînent.
  • Idéalement, conservez les paramètres dans un seul fichier. J'oublie des choses si je ne les regarde pas bien :)
  • Aucune modification manuelle à déployer. Doit pouvoir tester / pousser / déployer avec une seule commande de matrice.
  • Évitez les fuites de paramètres de développement dans la production.
  • Restez aussi proche que possible de la disposition Django "standard" (* toux *).

Je pensais que l'allumage de la machine hôte avait un certain sens, mais j'ai compris que le vrai problème ici était différents paramètres pour différents environnements , et j'ai eu un moment ah. J'ai mis ce code à la fin de mon fichier 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 cette façon, l'application utilise par défaut les paramètres de production, ce qui signifie que vous "mettez sur liste blanche" votre environnement de développement. Il est beaucoup plus sûr d'oublier de définir la variable d'environnement localement que si c'était l'inverse et vous avez oublié de définir quelque chose en production et de laisser certains paramètres de développement être utilisés.

Lors du développement local, à partir du shell ou dans un .bash_profile ou n'importe où:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(Ou si vous développez sous Windows, défini via le Panneau de configuration ou quel que soit son nom de nos jours ... Windows l'a toujours rendu si obscur que vous pouvez définir des variables d'environnement.)

Avec cette approche, les paramètres de développement sont tous au même endroit (standard) et remplacent simplement ceux de production là où cela est nécessaire. Tout déblayage avec des paramètres de développement doit être complètement sûr de s'engager dans le contrôle des sources sans impact sur la production.

Jason Boyd
la source
Mieux vaut simplement maintenir différents fichiers de configuration et choisir en utilisant la variable env standard DJango DJANGO_SETTINGS_MODULE
Rob Grant