Come gestire le impostazioni locali e di produzione in Django?


298

Qual è il modo consigliato di gestire le impostazioni per lo sviluppo locale e il server di produzione? Alcuni di essi (come costanti, ecc.) Possono essere modificati / accessibili in entrambi, ma alcuni (come percorsi di file statici) devono rimanere diversi e quindi non devono essere sovrascritti ogni volta che viene distribuito il nuovo codice.

Attualmente sto aggiungendo tutte le costanti a settings.py. Ma ogni volta che cambio una costante localmente, devo copiarla sul server di produzione e modificare il file per le modifiche specifiche della produzione ... :(

Modifica: sembra che non ci sia una risposta standard a questa domanda, ho accettato il metodo più popolare.



Dai un'occhiata alle configurazioni di django .
JJD,

2
Il metodo accettato non è più quello più popolare.
Daniel,

2
django-split-settings è molto facile da usare. Non richiede di riscrivere alcuna impostazione predefinita.
sobolevn,

dovresti usare il file base.py e nel tuo local.py "from .base import *", lo stesso nel tuo production.py "from .base import *", devi eseguire il tuo progetto con: python manage.py corsa server - settings = project_name.settings.local
Roberth Solís

Risposte:


127

In settings.py:

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

Puoi ignorare ciò di cui hai bisogno local_settings.py; allora dovrebbe rimanere fuori dal controllo della versione. Ma dato che dici di aver copiato, suppongo che tu non ne usi nessuno;)


3
Per facilitare il tracciamento / implementazione di nuove impostazioni, utilizzare un "local_settings.py" sui computer di produzione / test e nessuno sullo sviluppo.
John Mee,

8
È così che faccio - aggiungendo quelle righe alla fine di settings.py in modo che possano sovrascrivere le impostazioni predefinite
daonb

61
Questo approccio significa che hai un codice senza rivali in esecuzione in sviluppo e produzione. E ogni sviluppatore ha una base di codice diversa. Qui chiamo anti-pattern.
Pydanny,

8
@pydanny Il problema è che Django memorizza la sua configurazione nel file .py. Non puoi aspettarti che tutti gli sviluppatori e il server di produzione utilizzino le stesse impostazioni, quindi devi modificare questo file .py o implementare una soluzione alternativa (file .ini, ambiente ecc.).
Tupteq,

3
Preferisco chiamare il modulo settings_localpiuttosto local_settingsche raggrupparlo settings.pyin elenchi di cartelle in ordine alfabetico. Mantieni settings_local.pyil controllo della versione usando .gitignorecome credenziali non appartengono a Git. Immagina di acquistarli per caso. Tengo invece in git un file modello chiamato settings_local.py.txt.
Fmalina,

297

Two Scoops of Django: Best Practices for Django 1.5 suggerisce di utilizzare il controllo versione per i file delle impostazioni e di archiviarli in una directory separata:

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

Il base.pyfile contiene impostazioni comuni (come MEDIA_ROOT o ADMIN), mentre local.pye production.pyhanno impostazioni specifiche del sito:

Nel file di base settings/base.py:

INSTALLED_APPS = (
    # common apps...
)

Nel file delle impostazioni di sviluppo locale settings/local.py:

from project.settings.base import *

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

Nel file delle impostazioni di produzione del file settings/production.py:

from project.settings.base import *

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

Quindi quando esegui django, aggiungi l' --settingsopzione:

# 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

Gli autori del libro hanno anche messo su un modello di layout del progetto di esempio su Github.


62
Si noti che invece di utilizzare --settingsogni volta, è possibile impostare DJANGO_SETTINGS_MODULEenvvar. Funziona bene con, ad esempio, Heroku: impostalo globalmente sulla produzione, quindi esegui l'override con dev nel tuo file .env.
Simon Weber,

9
Usare DJANGO_SETTINGS_MODULEenv var è la migliore idea qui, grazie Simon.
Kibibu,

20
Potrebbe essere necessario modificare le BASE_DIRimpostazioni inos.path.dirname(os.path.realpath(os.path.dirname(__file__) + "/.."))
Petr Peller

5
@rsp secondo i documenti di django, importa from django.conf import settingsquale è un oggetto che estrae l'interfaccia e disaccoppia il codice dalla posizione delle impostazioni, docs.djangoproject.com/en/dev/topics/settings/…

3
Se imposto DJANGO_SETTINGS_MODULE tramite una variabile ambientale, ho ancora bisogno di os.environ.setdefault ("DJANGO_SETTINGS_MODULE", "projectname.settings.production") nel mio file wsgi.py? Inoltre, ho impostato la var ambientale usando: export DJANGO_SETTINGS_MODULE = projectname.settings.local, ma poi si perde quando chiudo il terminale. Cosa posso fare per assicurarmi che sia salvato? Devo aggiungere quella linea al file bashrc?
Kritz,

71

Invece di settings.py, utilizzare questo layout:

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

common.py è dove vive la maggior parte della tua configurazione.

prod.py importa tutto dal comune e sovrascrive tutto ciò di cui ha bisogno per sovrascrivere:

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

# Production overrides
DEBUG = False
#...

Allo stesso modo, dev.pyimporta tutto da common.pye ignora tutto ciò di cui ha bisogno per eseguire l'override.

Infine, __init__.pyè dove decidi quali impostazioni caricare, ed è anche dove memorizzi i segreti (quindi questo file non dovrebbe essere aggiornato):

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

Quello che mi piace di questa soluzione è:

  1. Tutto è nel tuo sistema di controllo delle versioni, tranne i segreti
  2. La maggior parte di configurazione è in un unico luogo: common.py.
  3. Le cose specifiche del prodotto entrano prod.py, le cose specifiche del dev entrano dev.py. È semplice.
  4. Puoi ignorare le cose da common.pydentro prod.pyo dev.py, e puoi ignorare qualsiasi cosa dentro __init__.py.
  5. È semplice pitone. Nessun hack di reimportazione.

2
Sto ancora cercando di capire cosa impostare nei miei file project.wsgi e manage.py per il file delle impostazioni. Farai luce su questo? In particolare, nel mio file manage.py ho os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings")foobar è una cartella con un __init__.pyfile e le impostazioni è una cartella con un __init__.pyfile che contiene i miei segreti e importa dev.py, che quindi importa common.py. EDIT Nevermind, non avevo installato un modulo necessario. Colpa mia! Funziona benissimo !!
teewuane,

5
Due cose: 1) meglio impostare Debug = True nel tuo dev.py piuttosto che = False nel tuo prod.py. 2) Invece di passare a init .py, passa utilizzando l'ambiente DJANGO_SETTINGS_MODULE var. Questo aiuterà con le distribuzioni PAAS (ad es. Heroku).
Rob Grant,

Quando utilizzo questa configurazione in django 1.8.4 e provo il server di esecuzione ricevo "django.core.exceptions.ImproperlyConfigured: l'impostazione SECRET_KEY non deve essere vuota.", Anche se ho SECRET_KEY sul mio file init .py. Mi sto perdendo qualcosa?
Polarcare,

l'uso di qualcosa come AWS_SECRET_ACCESS_KEY = os.getenv ("AWS_SECRET_ACCESS_KEY") non è più sicuro? Domanda onesta: so perché non vuoi che sia aggiornato, ma l'altra alternativa è ottenerlo dall'ambiente. Il che pone la domanda sull'impostazione della variabile d'ambiente, ovviamente, ma che può essere lasciata al meccanismo di implementazione, no?
JL Peyret,

20

Uso una versione leggermente modificata dello stile "if DEBUG" delle impostazioni pubblicate da Harper Shelby. Ovviamente a seconda dell'ambiente (win / linux / etc.) Potrebbe essere necessario modificare leggermente il codice.

In passato utilizzavo "if DEBUG" ma ho scoperto che occasionalmente dovevo fare dei test con DEUBG impostato su False. Quello che volevo davvero distinguere se l'ambiente fosse la produzione o lo sviluppo, che mi ha dato la libertà di scegliere il livello 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'

Considererei ancora questo modo di impostare un work in progress. Non ho visto alcun modo per gestire le impostazioni di Django che coprivano tutte le basi e allo stesso tempo non era una seccatura totale per l'installazione (non sono giù con i metodi dei file di impostazioni 5x).


Questo è il tipo di cosa che consente alle impostazioni di Django di essere un vero file di codice, e stavo suggerendo. Non ho fatto nulla di simile da solo, ma è sicuramente il tipo di soluzione che potrebbe essere una risposta generale migliore della mia.
Harper Shelby,

3
Mi sono appena imbattuto in questo per la prima volta e ho scelto di usare (con successo!) La tua soluzione, con una leggera differenza: ho usato uuid.getnode () per trovare il uuid del mio sistema. Quindi sto testando se uuid.getnode () == 12345678901 (in realtà un numero diverso) invece del test os.environ che hai usato. Non sono riuscito a trovare la documentazione per convincermi che os.environ ['COMPUTERNAME'] è unico per computer.
Joe Golton,

os.environ ['COMPUTERNAME'] non funziona su Amazon AWS Ubuntu. Ricevo un KeyError.
nu everest,

Quando si utilizza l'UUID, questa soluzione si è rivelata la migliore e la più semplice per me. Non richiede molti patchwork complicati e sovramodularizzati. In un ambiente di produzione, è ancora necessario posizionare le password del database e SECRET_KEY in un file separato che risiede al di fuori del controllo della versione.
nu everest,

os.environ['COMPUTERNAME']purtroppo non funziona su PythonAnywhere. Ottieni un KeyError.
nbeuchat,

14

Uso un settings_local.py e un settings_production.py. Dopo aver provato diverse opzioni ho scoperto che è facile perdere tempo con soluzioni complesse quando semplicemente avere due file di impostazioni è facile e veloce.

Quando usi mod_python / mod_wsgi per il tuo progetto Django devi puntarlo al tuo file delle impostazioni. Se lo punti a app / settings_local.py sul tuo server locale e app / settings_production.py sul tuo server di produzione, la vita diventa facile. Basta modificare il file delle impostazioni appropriato e riavviare il server (il server di sviluppo Django si riavvierà automaticamente).


2
E il server di sviluppo locale? c'è un modo per dire al webserver django (eseguito usando python manage.py runserver), quale file di impostazioni usare?
akv,

2
@akv se si aggiunge --settings = [nome modulo] (nessuna estensione .py) alla fine del comando RunServer è possibile specificare quale file di impostazioni utilizzare. Se hai intenzione di farlo, fai un favore a te stesso e crea uno script shell / file batch con le impostazioni di sviluppo configurate. Fidati di me, le tue dita ti ringrazieranno.
T. Stone,

questa è la soluzione che uso. l'hacking di un file di impostazioni da utilizzare sia per la produzione che per lo sviluppo è disordinato
George Godik,

4
Penso che sia meglio usare settings.py in fase di sviluppo, in quanto non è necessario specificarlo continuamente.
Andre Bossard,

Sono corretto nel presupporre che questo metodo richieda l'importazione del modulo delle impostazioni tramite il proxy, django.conf.settings? Altrimenti dovresti modificare le dichiarazioni di importazione per puntare al file di impostazioni corretto quando invii live.
Groady,

8

TL; DR: Il trucco è modificare os.environmentprima di importare settings/base.pyin qualsiasi settings/<purpose>.py, questo semplificherà notevolmente le cose.


Il solo pensiero di tutti questi file intrecciati mi fa venire il mal di testa. Combinare, importare (a volte in modo condizionale), sovrascrivere, applicare patch a ciò che era già stato impostato nel caso in cui le DEBUGimpostazioni fossero modificate in seguito. Che incubo!

Nel corso degli anni ho attraversato tutte le diverse soluzioni. Funzionano tutti in qualche modo , ma sono così dolorosi da gestire. WTF! Abbiamo davvero bisogno di tutta quella seccatura? Abbiamo iniziato con un solo settings.pyfile. Ora abbiamo bisogno di una documentazione solo per combinare correttamente tutte queste in un ordine corretto!

Spero di aver finalmente raggiunto il (mio) punto debole con la soluzione di seguito.

Ricapitoliamo gli obiettivi (alcuni comuni, altri miei)

  1. Mantenere segreti i segreti - non archiviarli in un repository!

  2. Imposta / leggi chiavi e segreti attraverso le impostazioni dell'ambiente, stile 12 fattori .

  3. Possibili impostazioni predefinite di fallback. Idealmente per lo sviluppo locale non è necessario altro oltre alle impostazioni predefinite.

  4. ... ma cerca di mantenere sicura la produzione di default. È meglio perdere un'impostazione locale, piuttosto che dover ricordare di regolare le impostazioni predefinite in modo sicuro per la produzione.

  5. Avere la possibilità di accendere DEBUG/ spegnere in un modo che può avere un effetto su altre impostazioni (ad es. Utilizzando JavaScript compresso o meno).

  6. Il passaggio tra le impostazioni degli scopi, come local / testing / staging / production, dovrebbe basarsi solo su DJANGO_SETTINGS_MODULE, niente di più.

  7. ... ma consenti un'ulteriore parametrizzazione attraverso impostazioni dell'ambiente come DATABASE_URL.

  8. ... consentono inoltre di utilizzare impostazioni di scopi diversi ed eseguirle localmente fianco a fianco, ad es. installazione di produzione su macchina sviluppatore locale, per accedere al database di produzione o testare i fogli di stile compressi del fumo.

  9. Fallire se una variabile d'ambiente non è impostata esplicitamente (richiede un valore vuoto al minimo), specialmente nella produzione, ad es. EMAIL_HOST_PASSWORD.

  10. Rispondere al DJANGO_SETTINGS_MODULEset predefinito in manage.py durante l'avvio del progetto django-admin

  11. Mantenere i condizionali al minimo, se la condizione è il tipo di ambiente designato (ad es. Per il file di registro del set di produzione e la sua rotazione), sovrascrivere le impostazioni nel file delle impostazioni finalizzato associato.

Non è

  1. Non lasciare che django legga le impostazioni di DJANGO_SETTINGS_MODULE da un file.
    Ugh! Pensa a quanto è meta. Se hai bisogno di avere un file (come docker env) leggilo nell'ambiente prima di avviare un processo di django.

  2. Non sovrascrivere DJANGO_SETTINGS_MODULE nel codice progetto / app, ad es. in base al nome host o al nome del processo.
    Se sei pigro per impostare la variabile di ambiente (come per setup.py test), fallo negli strumenti appena prima di eseguire il codice del progetto.

  3. Evita la magia e le patch di come django legge le sue impostazioni, preelabora le impostazioni ma non interferire in seguito.

  4. Nessuna assurdità basata sulla logica complicata. La configurazione deve essere fissa e materializzata non calcolata al volo. Fornire i valori predefiniti di fallback è una logica sufficiente qui.
    Vuoi davvero eseguire il debug, perché localmente hai un set di impostazioni corretto ma in produzione su un server remoto, su una delle centinaia di macchine, qualcosa calcolato in modo diverso? Oh! Test unitari? Per le impostazioni? Sul serio?

Soluzione

La mia strategia è costituito da eccellente django-environ utilizzato con inifile di stile, fornendo os.environment, alcuni minimi e brevi valori di default per lo sviluppo locale settings/<purpose>.pydei file che hanno una import settings/base.py dopo l' os.environmentstato fissato da un INIfile. Questo ci dà effettivamente una sorta di iniezione di impostazioni.

Il trucco qui è modificare os.environmentprima di importare settings/base.py.

Per vedere l'esempio completo vai al repository: 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

impostazioni / .env

Impostazioni predefinite per lo sviluppo locale. Un file segreto, per impostare principalmente le variabili di ambiente richieste. Impostali su valori vuoti se non sono richiesti nello sviluppo locale. Forniamo impostazioni predefinite qui e non in caso settings/base.pydi errore su qualsiasi altra macchina se mancano dall'ambiente.

impostazioni / local.py

Quello che succede qui è caricare l'ambiente da settings/.env, quindi importare le impostazioni comuni da settings/base.py. Successivamente, possiamo ignorarne alcuni per facilitare lo sviluppo locale.

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

impostazioni / production.py

Per la produzione non dovremmo aspettarci un file di ambiente, ma è più facile averne uno se stiamo testando qualcosa. Comunque, per non fornire poche impostazioni predefinite in linea, quindi settings/base.pypuò rispondere di conseguenza.

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

I principali punti di interesse qui sono DEBUGe ASSETS_DEBUGsovrascrivono, saranno applicati al pitone os.environSOLO se MANCANO dall'ambiente e dal file.

Questi saranno i nostri valori predefiniti di produzione, non è necessario inserirli nell'ambiente o nel file, ma possono essere sostituiti se necessario. ! Neat

impostazioni / base.py

Queste sono le tue impostazioni per lo più django alla vaniglia, con alcuni condizionali e molta lettura da parte dell'ambiente. Quasi tutto è qui, mantenendo tutti gli ambienti proposti coerenti e il più simile possibile.

Le differenze principali sono di seguito (spero che siano autoesplicative):

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

L'ultimo bit mostra la potenza qui. ASSETS_DEBUGha un valore predefinito ragionevole, che può essere ignorato settings/production.pye persino che può essere ignorato da un'impostazione ambientale! Sìì!

In effetti abbiamo una gerarchia mista di importanza:

  1. settings / .py - imposta i valori predefiniti in base allo scopo, non memorizza i segreti
  2. settings / base.py - è principalmente controllato dall'ambiente
  3. impostazioni dell'ambiente di processo - 12 fattori baby!
  4. settings / .env - impostazioni predefinite locali per un facile avvio

Ehi Janusz ... quindi nel file .env andrebbero tutte le chiavi API, le chiavi e le password di autenticazione ecc.? Proprio come TWILLIO_API = "abc123"? Oppure TWILLIO_API = env ("TWILLIO_API")?
Dbinott,

Sì, ma questo è solo un fallback per le impostazioni dell'ambiente. Questo file è utile per lo sviluppo ma non viene salvato in repository o trasferito in produzione dove è necessario utilizzare rigorosamente le impostazioni dell'ambiente o l'equivalente della piattaforma che a sua volta configurerà le impostazioni dell'ambiente per il processo del server.
Janusz Skonieczny,

7

Gestisco le mie configurazioni con l'aiuto di django-split-settings .

È una sostituzione drop-in per le impostazioni predefinite. È semplice, ma configurabile. E non è necessario il refactoring delle impostazioni esistenti.

Ecco un piccolo esempio (file 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()
    )

Questo è tutto.

Aggiornare

Ho scritto un post sul blog sulla gestione djangodelle impostazioni con django-split-sttings. Dare un'occhiata!


1
Ho provato che .. mi sono imbattuto in un muro una volta ho provato a eseguire i miei test di unità di django .. non riuscivo a capire come specificare da quale file di impostazioni leggere
abbood

Ho creato un'essenza
sobolevn

ho ottenuto qualcosa di simile a questo nel mio codice, così ho controllare il flag settings.DEBUG sapere se voglio importare roba .. quella bandiera è sempre impostato su false in Django unit test (vedi qui ), quindi il mio lavoro intorno è quello di ignorare loro a ogni test in questo modo
circa il

ecco un'altra domanda però: il mio uwsgi.inifile ha impostazioni diverse su dev / prod .. hai idea di come farlo scegliere i valori dal mio file di impostazioni?
circa

scusa, non ho la configurazione. puoi fare una domanda separata con maggiori dettagli e cercherò di aiutarti.
sobolevn,

6

Il problema con la maggior parte di queste soluzioni è che le impostazioni locali sono state applicate prima di quelle comuni o dopo di esse.

Quindi è impossibile ignorare cose come

  • le impostazioni specifiche di env definiscono gli indirizzi per il pool memcached e nel file delle impostazioni principali questo valore viene utilizzato per configurare il backend della cache
  • le impostazioni specifiche di env aggiungono o rimuovono app / middleware a quello predefinito

allo stesso tempo.

Una soluzione può essere implementata utilizzando file di configurazione in stile "ini" con la classe ConfigParser. Supporta più file, interpolazione di stringhe pigre, valori predefiniti e molte altre chicche. Una volta caricati un numero di file, è possibile caricare più file e i loro valori avranno la precedenza su quelli precedenti, se presenti.

Caricare uno o più file di configurazione, a seconda dell'indirizzo della macchina, delle variabili di ambiente e persino dei valori nei file di configurazione caricati in precedenza. Quindi basta usare i valori analizzati per popolare le impostazioni.

Una strategia che ho usato con successo è stata:

  • Carica un defaults.inifile predefinito
  • Controllare il nome della macchina e caricare tutti i file che corrispondono al nome di dominio completo invertito, dalla corrispondenza più breve alla corrispondenza più lunga (quindi, ho caricato net.ini, quindi net.domain.ini, quindi net.domain.webserver01.ini, ognuno possibilmente sovrascrivendo i valori del precedente). Questo account riguarda anche i computer degli sviluppatori, in modo che ognuno possa impostare il proprio driver di database preferito, ecc. Per lo sviluppo locale
  • Controlla se è stato dichiarato un "nome cluster" e, in tal caso cluster.cluster_name.ini, carica , che può definire cose come IP di database e cache

Come esempio di qualcosa che puoi ottenere con questo, puoi definire un valore "sottodominio" per-env, che viene quindi utilizzato nelle impostazioni predefinite (as hostname: %(subdomain).whatever.net) per definire tutti i nomi host e i cookie necessari per far funzionare django.

Questo è quanto potevo ottenere, la maggior parte dei file (esistenti) aveva solo 3 o 4 impostazioni. Inoltre, dovevo gestire la configurazione del cliente, quindi esisteva un set aggiuntivo di file di configurazione (con elementi come nomi di database, utenti e password, sottodominio assegnato ecc.), Uno o più per cliente.

Si può ridimensionare questo valore come basso o alto quanto basta, basta inserire nel file di configurazione le chiavi che si desidera configurare per ambiente e, una volta che è necessaria una nuova configurazione, inserire il valore precedente nella configurazione predefinita e sovrascriverlo dove necessario.

Questo sistema si è dimostrato affidabile e funziona bene con il controllo della versione. È stato usato per molto tempo nella gestione di due cluster separati di applicazioni (15 o più istanze separate del sito django per macchina), con oltre 50 clienti, in cui i cluster stavano cambiando dimensione e membri a seconda dell'umore del sistema. .


1
Hai un esempio di come carichi le impostazioni dall'iniolo nelle impostazioni di Django?
Kaleissin,

Vedi docs.python.org/2/library/configparser.html . È possibile caricare un parser con config = ConfigParser.ConfigParser() quindi leggere i file config.read(array_of_filenames)e ottenere valori utilizzando config.get(section, option). Quindi prima carichi la tua configurazione e poi la usi per leggere i valori per le impostazioni.
riscritto

5

Sto anche lavorando con Laravel e mi piace l'implementazione lì. Ho cercato di imitarlo e combinarlo con la soluzione proposta da T. Stone (vedi sopra):

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

Forse qualcosa del genere ti aiuterebbe.


4

Ricorda che settings.py è un file di codice live. Supponendo che DEBUG non sia impostato sulla produzione (che è una buona pratica), puoi fare qualcosa del tipo:

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

Abbastanza semplice, ma in teoria si potrebbe arrivare a qualsiasi livello di complessità basandosi solo sul valore di DEBUG - o su qualsiasi altra variabile o controllo del codice che si desidera utilizzare.


4

Per la maggior parte dei miei progetti utilizzo il seguente modello:

  1. Crea settings_base.py dove memorizzo le impostazioni comuni a tutti gli ambienti
  2. Ogni volta che devo utilizzare un nuovo ambiente con requisiti specifici, creo un nuovo file di impostazioni (ad es. Settings_local.py) che eredita il contenuto di settings_base.py e sovrascrive / aggiunge le variabili di impostazione appropriate ( from settings_base import *)

(Per eseguire manage.py con impostazioni personalizzate di file è sufficiente utilizzare --settings opzione del comando: manage.py <command> --settings=settings_you_wish_to_use.py)


3

La mia soluzione a questo problema è anche un po 'un mix di alcune soluzioni già indicate qui:

  • Tengo un file chiamato local_settings.pyche ha il contenuto USING_LOCAL = Truein dev e USING_LOCAL = Falsein prod
  • In settings.pyfaccio un'importazione su quel file per ottenere l' USING_LOCALimpostazione

Quindi baso tutte le mie impostazioni dipendenti dall'ambiente su quella:

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

Preferisco questo avere due file settings.py separati che devo mantenere in quanto posso mantenere le mie impostazioni strutturate in un singolo file più facile rispetto alla loro diffusione su più file. In questo modo, quando aggiorno un'impostazione, non dimentico di farlo per entrambi gli ambienti.

Naturalmente ogni metodo ha i suoi svantaggi e questo non fa eccezione. Il problema qui è che non riesco a sovrascrivere il local_settings.pyfile ogni volta che invio le mie modifiche alla produzione, il che significa che non posso semplicemente copiare tutti i file alla cieca, ma è qualcosa con cui posso convivere.


3

Uso una variante di ciò che jpartogi menzionato sopra, che trovo un po 'più breve:

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)

Fondamentalmente su ogni computer (sviluppo o produzione) ho il file hostname_settings.py appropriato che viene caricato dinamicamente.



3

1 - Crea una nuova cartella all'interno della tua app e le impostazioni del nome.

2 - Ora crea un nuovo __init__.pyfile al suo interno e al suo interno scrivi

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 - Creare tre nuovi file nel nome della cartella delle impostazioni local.pye production.pye base.py.

4 - All'interno base.py, copia tutto il contenuto della settings.pycartella precedente e rinominalo con qualcosa di diverso, diciamo old_settings.py.

5 - In base.py cambia il tuo percorso BASE_DIR per puntare al tuo nuovo percorso di impostazione

Vecchio percorso-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Nuovo percorso -> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

In questo modo, la direzione del progetto può essere strutturata e gestibile tra produzione e sviluppo locale.


2

Per utilizzare una settingsconfigurazione diversa su un ambiente diverso, creare un file di impostazioni diverso. E nello script di distribuzione, avviare il server utilizzando il --settings=<my-settings.py>parametro, tramite il quale è possibile utilizzare impostazioni diverse in ambienti diversi.

Vantaggi dell'utilizzo di questo approccio :

  1. Le tue impostazioni saranno modulari in base a ciascun ambiente

  2. È possibile importare il master_settings.pycontenente la configurazione di base in environmnet_configuration.pye sovrascrivere i valori che si desidera modificare in quell'ambiente.

  3. Se hai un team enorme, ogni sviluppatore può avere il proprio local_settings.pyche può aggiungere al repository di codice senza alcun rischio di modificare la configurazione del server. Puoi aggiungere queste impostazioni locali a .gitnorese usi git o .hginorese Mercurial for Version Control (o qualsiasi altro). In questo modo le impostazioni locali non faranno nemmeno parte della base di codice effettiva per mantenerlo pulito.


2

Le mie impostazioni sono state suddivise come segue

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

Abbiamo 3 ambienti

  • dev
  • messa in scena
  • produzione

Ora ovviamente la messa in scena e la produzione dovrebbero avere il massimo ambiente simile possibile. Quindi abbiamo continuato prod.pyper entrambi.

Ma c'era un caso in cui dovevo identificare il server in esecuzione è un server di produzione. @T. La risposta di Stone mi ha aiutato a scrivere un assegno come segue.

from socket import gethostname, gethostbyname  
PROD_HOSTS = ["webserver1", "webserver2"]

DEBUG = False
ALLOWED_HOSTS = [gethostname(), gethostbyname(gethostname()),]


if any(host in PROD_HOSTS for host in ALLOWED_HOSTS):
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True  

1

L'ho differenziato in manage.py e ho creato due file di impostazioni separati: local_settings.py e prod_settings.py.

In manage.py controllo se il server è un server locale o un server di produzione. Se si tratta di un server locale caricherà local_settings.py ed è un server di produzione caricherà prod_settings.py. Fondamentalmente questo è come sarebbe:

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

Ho trovato più facile separare il file delle impostazioni in due file separati invece di fare molti if all'interno del file delle impostazioni.


1

In alternativa per mantenere un file diverso se lo farai: se stai usando git o qualsiasi altro VCS per inviare codici da locale a server, quello che puoi fare è aggiungere il file delle impostazioni a .gitignore.

Ciò ti consentirà di avere contenuti diversi in entrambi i luoghi senza alcun problema. SO sul server è possibile configurare una versione indipendente di settings.py e qualsiasi modifica apportata sul locale non si rifletterà sul server e viceversa.

Inoltre, rimuoverà anche il file settings.py da github, il grosso difetto, che ho visto fare molti neofiti.



0

Penso che la soluzione migliore sia suggerita da @T. Stone, ma non so perché semplicemente non usare la bandiera DEBUG in Django. Scrivo il codice seguente per il mio sito Web:

if DEBUG:
    from .local_settings import *

Le soluzioni semplici sono sempre migliori di quelle complesse.


-2

Ho trovato le risposte qui molto utili. (È stato risolto in modo più definitivo? L'ultima risposta è stata un anno fa.) Dopo aver considerato tutti gli approcci elencati, ho trovato una soluzione che non ho visto elencato qui.

I miei criteri erano:

  • Tutto dovrebbe essere nel controllo del codice sorgente. Non mi piacciono i pezzi difficili.
  • Idealmente, mantieni le impostazioni in un file. Dimentico le cose se non le guardo bene :)
  • Nessuna modifica manuale da distribuire. Dovrebbe essere in grado di testare / spingere / distribuire con un singolo comando fabric.
  • Evita di perdere le impostazioni di sviluppo nella produzione.
  • Resta il più vicino possibile al layout Django "standard" (* tosse *).

Ho pensato che accendere la macchina host avesse un senso, ma poi ho capito che il vero problema qui è impostazioni diverse per ambienti diversi , e ho avuto un momento aha. Ho inserito questo codice alla fine del mio file 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'

In questo modo, l'app assume le impostazioni di produzione predefinite , il che significa che stai "esplicitando" esplicitamente il tuo ambiente di sviluppo. È molto più sicuro dimenticare di impostare la variabile di ambiente localmente che se fosse il contrario e ti sei dimenticato di impostare qualcosa in produzione e lasciare che vengano utilizzate alcune impostazioni di sviluppo.

Quando si sviluppa localmente, dalla shell o in un .bash_profile o ovunque:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(O se stai sviluppando su Windows, imposta tramite il Pannello di controllo o come si chiama in questi giorni ... Windows lo ha sempre reso così oscuro da poter impostare le variabili di ambiente.)

Con questo approccio, le impostazioni di sviluppo sono tutte in un unico posto (standard) e semplicemente sostituiscono quelle di produzione dove necessario. Qualsiasi confusione con le impostazioni di sviluppo dovrebbe essere completamente sicura per impegnarsi nel controllo del codice sorgente senza alcun impatto sulla produzione.


Meglio mantenere solo diversi file di configurazione e scegliere usando la variabile env standard DJango DJANGO_SETTINGS_MODULE
Rob Grant
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.