Django: come gestire le impostazioni di sviluppo e produzione?


129

Sto sviluppando un'app di base. Ora, nella fase di distribuzione, è diventato chiaro che ho bisogno sia di impostazioni locali che di impostazioni di produzione.

Sarebbe bello sapere quanto segue:

  • Il modo migliore per affrontare le impostazioni di sviluppo e produzione.
  • Come mantenere app come django-debug-toolbar solo in un ambiente di sviluppo.
  • Eventuali altri suggerimenti e best practice per le impostazioni di sviluppo e distribuzione.

Risposte:


109

La DJANGO_SETTINGS_MODULEvariabile d'ambiente controlla quale file di impostazioni Django caricherà.

Si creano quindi file di configurazione separati per i rispettivi ambienti (si noti che possono ovviamente entrambi import *da un file separato, "impostazioni condivise") e si utilizzano DJANGO_SETTINGS_MODULEper controllare quale utilizzare.

Ecco come:

Come notato nella documentazione di Django:

Il valore di DJANGO_SETTINGS_MODULE dovrebbe essere nella sintassi del percorso Python, ad esempio mysite.settings. Nota che il modulo delle impostazioni dovrebbe trovarsi nel percorso di ricerca dell'importazione Python.

Quindi, supponiamo che tu abbia creato myapp/production_settings.pye myapp/test_settings.pynel tuo repository di origine.

In tal caso, dovresti impostare rispettivamente DJANGO_SETTINGS_MODULE=myapp.production_settingsdi utilizzare il primo e DJANGO_SETTINGS_MODULE=myapp.test_settingsdi utilizzare il secondo.


Da qui in poi, il problema si riduce all'impostazione della DJANGO_SETTINGS_MODULEvariabile d'ambiente.

Impostazione DJANGO_SETTINGS_MODULEutilizzando uno script o una shell

È quindi possibile utilizzare uno script di bootstrap o un manager di processo per caricare le impostazioni corrette (impostando l'ambiente), o semplicemente eseguirlo dalla shell prima di iniziare Django: export DJANGO_SETTINGS_MODULE=myapp.production_settings.

Nota che puoi eseguire questa esportazione in qualsiasi momento da una shell: non è necessario che risieda nel tuo file .bashrc.

Impostazione DJANGO_SETTINGS_MODULEutilizzando un Process Manager

Se non ti piace scrivere uno script di bootstrap che imposta l'ambiente (e ci sono ottime ragioni per sentirti in questo modo!), Ti consiglio di utilizzare un gestore di processi:


Infine, ricordiamo che si può sfruttare la PYTHONPATHvariabile per memorizzare le impostazioni in un luogo completamente diverso (ad esempio, su un server di produzione, la loro memorizzazione in/etc/ ). Ciò consente di separare la configurazione dai file dell'applicazione. Potresti volerlo o meno, dipende da come è strutturata la tua app.


7
Per chiarire, dal momento che il settings.pyfile è archiviato SiteName/settings.pydi default, se metti i tuoi file di impostazioni alternativi nella stessa directory, la riga aggiunta a bin / attivare dovrebbe leggere DJANGO_SETTINGS_MODULE="SiteName.test_settings"Altrimenti ottima risposta!
alexbhandari

2
per coincidenza conosci un tutorial su come eseguire questo passo dopo passo, sono nuovo di Django e non so dove impostare DJANGO_SETTINGS_MODULE o PYTHONPATH
Jesus Almaral - Hackaprende

Questa soluzione non sembra valere per un conda env. Non è presente alcun bin / attivare in un ambiente conda.
Pouya Yousefi

1
@PouyaYousefi: non è assolutamente necessario utilizzare virtualenv per utilizzare questa risposta. La risposta si riduce in realtà a due passaggi: a) utilizzare file di impostazioni separati eb) utilizzare DJANGO_SETTINGS_MODULEper scegliere quello che si desidera utilizzare. Modificare bin/activate è uno per fare quest'ultimo (TBH, non penso più che sia comunque una buona idea, quindi l'ho tolto), ma non è l'unico.
Thomas Orozco

È anche utile se stai usando Django nell'edizione della community di pycharm e devi eseguire gli unit test sia sulla riga di comando che sulla community di pycharm correttamente. Supponi di aver creato un solo file di configurazione semplice in myapp / settings.py nel tuo repository di origine. In tal caso, impostare "DJANGO_SETTINGS_MODULE = myapp.settings" nel menu RUN / Modifica configurazione / Variabile d'ambiente per usarlo per eseguire casi di test.
F.Tamy

58

Per impostazione predefinita, utilizza le impostazioni di produzione, ma crea un file chiamato settings_dev.pynella stessa cartella del settings.pyfile. Aggiungi le sostituzioni lì, comeDEBUG=True .

Sul computer che verrà utilizzato per lo sviluppo, aggiungi questo al tuo ~/.bashrcfile:

export DJANGO_DEVELOPMENT=true

In fondo al settings.pyfile, aggiungi quanto segue.

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

(Nota che importando * dovrebbe essere generalmente evitata in Python)

Per impostazione predefinita, i server di produzione non sovrascriveranno nulla. Fatto!

Rispetto alle altre risposte, questa è più semplice perché non necessita di aggiornamento PYTHONPATH, o settaggio DJANGO_SETTINGS_MODULEche ti permette di lavorare solo su un progetto django alla volta.


8
come è questa non la risposta corretta? COSÌ è davvero un casino al giorno d'oggi. Ty cs01
codyc4321

if os.environ.get('DJANGO_DEVELOPMENT', 'true')funziona anche. Dico questo solo perché il is not truemetodo sopra non è riuscito a importare per me su Python 3.6.
brt

1
@brt questa è una cattiva idea: userà sempre le tue DEVimpostazioni che faranno trapelare dati privati ​​su un server pubblico. Vuoi davvero solo controllare che la DJANGO_DEVELOPMENTvariabile d'ambiente esista (cioè is not None).
cs01

Grazie per le informazioni, @ cs01. Mi sono reso conto di aver fatto qualcosa di sbagliato quando ho fatto saltare in aria il mio sito con il caricamento delle impostazioni errate, ma non ero sicuro del motivo per cui settings_dev.pyveniva caricato sul server.
brt

2
@ cs01 Mi spingerei al punto di assicurarmi che esista e sia veritiero, semplicemente consegnando l' is not Noneassegno. Inoltre os.getenvè la stenografia
Tjorriemorrie

35

Di solito ho un file di impostazioni per ambiente e un file di impostazioni condiviso:

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

Ciascuno dei miei file di ambiente ha:

try:
    from shared_settings import *
except ImportError:
    pass

Ciò mi consente di sovrascrivere le impostazioni condivise se necessario (aggiungendo le modifiche sotto quella stanza).

Quindi seleziono i file delle impostazioni da utilizzare collegandoli a settings.py:

ln -s settings.development.py settings.py

2
Come affronti il ​​divieto di pep8 import *? Disabiliti quel controllo? Ho inserito questa importazione in un exec()ma poi non posso avere condizionali su variabili che non sono definite in questo file, né posso alterare la INSTALLED_APPSvariabile perché è "non definita"
Mikhail

11
Non filtriamo i nostri file di impostazioni, perché non sono tanto codice quanto sono configurazioni espresse in Python.
Daniel Watkins

17

Ecco come lo faccio in 6 semplici passaggi:

  1. Crea una cartella nella directory del tuo progetto e assegnagli un nome settings.

    Struttura del progetto:

    myproject/
           myapp1/
           myapp2/              
           myproject/
                  settings/
  2. Crea quattro file python all'interno della settingsdirectory __init__.py, ovvero base.py, dev.pyeprod.py

    File delle impostazioni:

    settings/
         __init__.py
         base.py
         prod.py
         dev.py 
  3. Aprilo __init__.pye riempilo con il seguente contenuto:

    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. Aprilo base.pye riempilo con tutte le impostazioni comuni (che verranno utilizzate sia in produzione che in sviluppo.) Ad esempio:

    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. Apri dev.pye includi quelle cose che sono specifiche per lo sviluppo, ad esempio:

    dev.py:

    DEBUG = True
    ALLOWED_HOSTS = ['localhost']
    ...
  6. Apri prod.pye includi quelle cose che sono specifiche per la produzione, ad esempio:

    prod.py:

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

10

Crea più settings*.pyfile, estrapolando le variabili che devono cambiare per ambiente. Quindi alla fine del tuo settings.pyfile master :

try:
  from settings_dev import *
except ImportError:
  pass

Tieni il separato settings_* file per ogni fase.

Nella parte superiore del settings_dev.pyfile, aggiungi questo:

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

Per importare variabili che devi modificare.

Questa voce del wiki contiene altre idee su come dividere le tue impostazioni.


Grazie Burham! Quando si distribuisce l'app, è sufficiente rimuovere il file settings_dev per vedere le mie impostazioni di distribuzione in azione?
Kristian Roebuck

Sì, o sostituire l'importazione consettings_prod.py
Burhan Khalid

1
Tuttavia, la modifica del file settings.py principale in una distribuzione significa che si scontrerà con il controllo della versione, quindi non è necessariamente la soluzione migliore. Direi che l'opzione di Thomas Orozco è la migliore: puoi impostare DJANGO_SETTINGS_MODULE nel tuo script di postattivazione virtualenv o nella configurazione gunicorn o mod_wsgi
Steve Jalim

1
Forse dovrebbe essere menzionato che non aggiungi mai al controllo del codice sorgente i file specifici dello stage. Ho pensato che si capisse che non avresti spostato impostazioni specifiche per una fase di un progetto.
Burhan Khalid

Se utilizzi virtualenv, in genere le impostazioni predefinite saranno {{project_name}}. Quindi "settings" non sarà una chiave in sys.modules. Sarà "myproject.settings" (o qualunque sia il nome del tuo progetto). È possibile utilizzare modname = "%s.settings" % ".".join(__name__.split('.')[:-1])per ottenere il nome completo del modulo e quindi globals().update(vars(sys.modules[modname])). Trovo che funzioni bene per me. Ovviamente rinunciare alla parte relativa alla determinazione programmatica del nome del modulo a favore di una stringa probabilmente funzionerebbe anche nella maggior parte dei casi.
Eric

9

Uso le fantastiche configurazioni django e tutte le impostazioni sono memorizzate nel mio 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

Per configurare il progetto Django ho semplicemente seguito la documentazione .


7

Ecco l'approccio che utilizziamo:

  • un settingsmodulo per dividere le impostazioni in più file per la leggibilità;
  • un .env.jsonfile per memorizzare credenziali e parametri che vogliamo escludere dal nostro repository git o che sono specifici dell'ambiente;
  • un env.pyfile per leggere il .env.jsonfile

Considerando la seguente struttura:

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

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

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

Possiamo avere le seguenti impostazioni:

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

i vantaggi di questa soluzione sono:

  • credenziali e configurazioni specifiche dell'utente per lo sviluppo locale senza modificare il repository git;
  • configurazione specifica dell'ambiente , si possono avere ad esempio tre ambienti differenti con tre differenti .env.jsoncome dev, stagging e production;
  • le credenziali non sono nel repository

Spero che questo aiuti, fammi sapere se vedi qualche avvertimento con questa soluzione.


supponendo dove envsostituirlo con dev, prodecc.? Cosa c'è nel vecchio settings.pyfile? Cosa c'è dentro storage.pye database.py?
dbinott

Ciao @dbinott, puoi aggiornare facilmente il env.pyfile in modo che tu possa scegliere, con una variabile d'ambiente, quale file caricare
Charlesthk

Ad esempio: conf = os.environ.get ('CONF', '') file_ = f ".env. {Conf} .json"
Charlesthk

Perché dovresti json rispetto a un tipo di dati Python nativo?
attacco aereo il

4

Uso la seguente struttura dei file:

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

Quindi __init__.pyun collegamento (ln in unix o mklink in Windows) a local.pyo può essere a, prod.pyquindi la configurazione è ancora nel project.settingsmodulo è pulita e organizzata, e se vuoi usare una configurazione particolare puoi usare la variabile d'ambiente DJANGO_SETTINGS_MODULEperproject.settings.prod se ne hai bisogno per eseguire un comando per l'ambiente di produzione.

Nei file prod.pye local.py:

from .shared import *

DATABASE = {
    ...
}

e il shared.pyfile rimane globale senza configurazioni specifiche.


3

costruire la risposta di cs01:

se hai problemi con la variabile d'ambiente, imposta il suo valore su una stringa (es. l'ho fatto DJANGO_DEVELOPMENT="true").

Ho anche modificato il flusso di lavoro dei file di cs01 come segue:

#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

In questo modo, Django non deve leggere l'intero file delle impostazioni prima di eseguire il file delle impostazioni appropriato. Questa soluzione è utile se il tuo file di produzione necessita di cose che si trovano solo sul tuo server di produzione.

Nota: in Python 3, i file importati devono avere un .allegato (ad esempio from .settings_dev import *)


1

Se vuoi mantenere 1 file di impostazioni e il tuo sistema operativo di sviluppo è diverso dal tuo sistema operativo di produzione, puoi metterlo in fondo al tuo 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

Ulteriori informazioni: come si controlla il sistema operativo in Python?


1

Sembra che sia stata data una risposta, tuttavia un metodo che uso in combinazione con il controllo della versione è il seguente:

Imposta un file env.py nella stessa directory delle impostazioni sul mio ambiente di sviluppo locale che aggiungo anche 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

Trovo che funzioni ed è molto più elegante: con env.py è facile vedere le nostre variabili di ambiente locali e possiamo gestire tutto questo senza più file settings.py o simili. Questo metodo consente di utilizzare tutti i tipi di variabili di ambiente locali che non vorremmo impostare sul nostro server di produzione. Utilizzando .gitignore tramite il controllo della versione, stiamo anche mantenendo tutto perfettamente integrato.


La soluzione più semplice. Si può anche definire tutto in una Configclasse all'interno del env.pyfile. Quindi invece di un import *, il modulo può essere importato da from env import Config. In questo modo, inoltre, non è necessario utilizzarlo se il os.pathcontrollo rende l'intera operazione molto più semplice.
Siddharth Pant

0

Utilizzare settings.pyper la produzione. Nella stessa directory creare settings_dev.pyper le sostituzioni.

# settings_dev.py

from .settings import * 

DEBUG = False

Su una macchina di sviluppo esegui la tua app Django con:

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

Su una macchina produttrice corri come se avessi avuto settings.pye nient'altro.

VANTAGGI

  1. settings.py (utilizzato per la produzione) è completamente indipendente dal fatto che esistono anche altri ambienti.
  2. Per vedere la differenza tra prod e dev basta guardare in un'unica posizione - settings_dev.py. Non c'è bisogno di raccogliere le configurazioni sparsi in tutto settings_prod.py, settings_dev.pyesettings_shared.py .
  3. Se qualcuno aggiunge un'impostazione alla configurazione del prodotto dopo aver risolto un problema di produzione, puoi essere certo che apparirà anche nella configurazione dello sviluppatore (a meno che non venga esplicitamente sovrascritto). Quindi la divergenza tra i diversi file di configurazione sarà ridotta al minimo.

0

Per il problema dell'impostazione dei file, scelgo di copiare

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

Quando esegui django, verrà eseguito __init__py. In questo momento, settings.py in setting1_dirsostituiràsettings.py in Project .

Come scegliere un ambiente diverso?

  • modificare __init__.py direttamente.
  • crea un file bash da modificare __init__.py .
  • modificare env in linux, quindi __init__.pyleggere questa variabile.

Perché usarlo in questo modo?

Poiché non mi piacciono così tanti file nella stessa directory, troppi file confonderanno gli altri partner e non molto bene per IDE. (IDE non riesce a trovare quale file usiamo)

Se non vuoi vedere tutti questi dettagli, puoi dividere il progetto in due parti.

  1. crea il tuo piccolo strumento come Spring Initializr, solo per impostare il tuo progetto. (fai qc come copiare il file)
  2. il codice del progetto

0

Sto usando un file app.yaml diverso per modificare la configurazione tra gli ambienti nel motore di app cloud di Google.

Puoi usarlo per creare una connessione proxy nel comando del tuo terminale:

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

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

File: 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]

-1

Questa è la mia soluzione, con ambienti diversi per dev, test e prod

import socket

[...]

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

if host_name == DEV_PC:
   #do something
   pass
elif [...]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.