Wie verwalte ich lokale und Produktionseinstellungen in Django?

298

Wie werden Einstellungen für die lokale Entwicklung und den Produktionsserver empfohlen? Einige von ihnen (wie Konstanten usw.) können in beiden geändert / aufgerufen werden, aber einige von ihnen (wie Pfade zu statischen Dateien) müssen unterschiedlich bleiben und sollten daher nicht jedes Mal überschrieben werden, wenn der neue Code bereitgestellt wird.

Derzeit füge ich alle Konstanten hinzu settings.py. Aber jedes Mal, wenn ich eine Konstante lokal ändere, muss ich sie auf den Produktionsserver kopieren und die Datei für produktionsspezifische Änderungen bearbeiten ... :(

Bearbeiten: Anscheinend gibt es keine Standardantwort auf diese Frage. Ich habe die beliebteste Methode akzeptiert.

akv
quelle
Bitte schauen Sie sich die Django-Konfigurationen an .
JJD
2
Die akzeptierte Methode ist nicht mehr die beliebteste.
Daniel
2
django-split-settings ist sehr einfach zu bedienen. Es ist nicht erforderlich, Standardeinstellungen neu zu schreiben.
Sobolevn
Sie sollten die Datei base.py verwenden und in Ihrer local.py "from .base import *", genauso wie in Ihrer Production.py "from .base import *", Ihr Projekt ausführen mit: python manage.py runserver - settings = project_name.settings.local
Roberth Solís

Antworten:

127

In settings.py:

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

Sie können überschreiben, was in benötigt wird local_settings.py. es sollte dann außerhalb Ihrer Versionskontrolle bleiben. Aber da Sie das Kopieren erwähnen, verwenden Sie vermutlich keine;)

Oh nein
quelle
3
Verwenden Sie eine "local_settings.py" auf den Produktions- / Testmaschinen und keine in der Entwicklung, um die Nachverfolgung / Bereitstellung neuer Einstellungen zu vereinfachen.
John Mee
8
So mache ich das - Hinzufügen dieser Zeilen am Ende von settings.py, damit sie die Standardeinstellungen überschreiben können
daonb
61
Dieser Ansatz bedeutet, dass in der Entwicklung und Produktion nicht versionierter Code ausgeführt wird. Und jeder Entwickler hat eine andere Codebasis. Ich nenne hier Anti-Pattern.
Pydanny
8
@pydanny Das Problem ist, dass Django seine Konfiguration in der .py-Datei speichert. Sie können nicht erwarten, dass alle Entwickler und Produktionsserver dieselben Einstellungen verwenden. Daher müssen Sie diese .py-Datei ändern oder eine alternative Lösung (INI-Dateien, Umgebung usw.) implementieren.
Tupteq
3
Ich ziehe es rufe das Modul settings_localim Gegensatz zu local_settingsgruppieren sie mit settings.pyin alphabetischen Ordnerlisten. Halten Sie settings_local.pysich von der Versionskontrolle fern, .gitignoreda Anmeldeinformationen nicht zu Git gehören. Stellen Sie sich vor, Sie beziehen sie versehentlich aus offenen Quellen. Ich halte settings_local.py.txtstattdessen eine Vorlagendatei namens git .
Zeilenumbruch
297

Zwei Scoops von Django: Best Practices für Django 1.5 empfiehlt, die Versionskontrolle für Ihre Einstellungsdateien zu verwenden und die Dateien in einem separaten Verzeichnis zu speichern:

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

Die base.pyDatei enthält allgemeine Einstellungen (wie MEDIA_ROOT oder ADMIN) local.pyund verfügt production.pyüber ortsspezifische Einstellungen:

In der Basisdatei settings/base.py:

INSTALLED_APPS = (
    # common apps...
)

In der lokalen Entwicklungseinstellungsdatei settings/local.py:

from project.settings.base import *

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

In der Datei mit den Einstellungen für die Dateiproduktion settings/production.py:

from project.settings.base import *

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

Wenn Sie dann Django ausführen, fügen Sie die --settingsOption hinzu:

# 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

Die Autoren des Buches haben auch eine Beispielvorlage für das Projektlayout auf Github erstellt.

gene_wood
quelle
62
Beachten Sie, dass --settingsSie die DJANGO_SETTINGS_MODULEEnvvar nicht jedes Mal verwenden, sondern festlegen können. Dies funktioniert gut mit z. B. Heroku: Stellen Sie es global auf Produktion ein und überschreiben Sie es dann mit dev in Ihrer .env-Datei.
Simon Weber
9
Die Verwendung von DJANGO_SETTINGS_MODULEenv var ist hier die beste Idee, danke Simon.
Kibibu
20
Möglicherweise müssen Sie die BASE_DIREinstellungen aufos.path.dirname(os.path.realpath(os.path.dirname(__file__) + "/.."))
Petr Peller
5
@rsp Gemäß den Django-Dokumenten importieren Sie from django.conf import settingsein Objekt, das die Schnittstelle abstrahiert und den Code vom Speicherort der Einstellungen entkoppelt. docs.djangoproject.com/de/dev/topics/settings/…
3
Wenn ich DJANGO_SETTINGS_MODULE über eine Umgebungsvariable festlegen möchte, benötige ich dann noch os.environ.setdefault ("DJANGO_SETTINGS_MODULE", "projectname.settings.production") in meiner Datei wsgi.py? Außerdem habe ich die Umgebungsvariable wie folgt festgelegt: export DJANGO_SETTINGS_MODULE = projectname.settings.local, aber dann geht sie verloren, wenn ich das Terminal schließe. Was kann ich tun, um sicherzustellen, dass es gespeichert wird? Soll ich diese Zeile zur bashrc-Datei hinzufügen?
Kritz
71

settings.pyVerwenden Sie stattdessen dieses Layout:

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

common.py Hier lebt der größte Teil Ihrer Konfiguration.

prod.py importiert alles von common und überschreibt alles, was zum Überschreiben benötigt wird:

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

# Production overrides
DEBUG = False
#...

In ähnlicher Weise wird dev.pyalles importiert und alles common.pyüberschrieben, was zum Überschreiben benötigt wird.

Schließlich __init__.pyentscheiden Sie, welche Einstellungen geladen werden sollen, und wo Sie Geheimnisse speichern (daher sollte diese Datei nicht versioniert werden):

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

Was mir an dieser Lösung gefällt, ist:

  1. Alles ist in Ihrem Versionierungssystem, außer Geheimnisse
  2. Die meisten Konfigurationen befinden sich an einem Ort : common.py.
  3. Produktspezifische Dinge gehen prod.pyrein, dev-spezifische Dinge gehen rein dev.py. Es ist einfach.
  4. Sie können Inhalte von common.pyin prod.pyoder dev.pyüberschreiben, und Sie können alles von in überschreiben __init__.py.
  5. Es ist einfach Python. Keine Re-Import-Hacks.
MiniQuark
quelle
2
Ich versuche immer noch herauszufinden, was in meinen Dateien project.wsgi und manage.py für die Einstellungsdatei festgelegt werden soll. Wirst du etwas Licht ins Dunkel bringen? Insbesondere in meiner Datei "manage.py" ist os.environ.setdefault("DJANGO_SETTINGS_MODULE", "foobar.settings")foobar ein Ordner mit einer __init__.pyDatei, und settings ist ein Ordner mit einer __init__.pyDatei, die meine Geheimnisse enthält und dev.py importiert, die dann common.py importiert. EDIT Nevermind, ich hatte kein Modul installiert, das benötigt wurde. Mein Fehler! Das funktioniert super !!
Teewuane
5
Zwei Dinge: 1) Besser Debug = True in Ihrer dev.py als = False in Ihrer prod.py. 2) Wechseln Sie nicht in init .py, sondern in der Umgebung DJANGO_SETTINGS_MODULE var. Dies hilft bei PAAS-Bereitstellungen (z. B. Heroku).
Rob Grant
Wenn ich dieses Setup in django 1.8.4 verwende und runserver versuche, erhalte ich "django.core.exceptions.ImproperlyConfigured: Die Einstellung SECRET_KEY darf nicht leer sein.", Auch wenn ich SECRET_KEY in meiner init .py-Datei habe. Vermisse ich etwas
Polarcare
Ist die Verwendung von AWS_SECRET_ACCESS_KEY = os.getenv ("AWS_SECRET_ACCESS_KEY") nicht sicherer? Ehrliche Frage - Ich weiß, warum Sie nicht wollen, dass es versioniert wird, aber die andere Alternative ist, es aus der Umgebung zu holen. Was wirft natürlich die Frage auf, die Umgebungsvariable festzulegen, aber das kann Ihrem Bereitstellungsmechanismus überlassen bleiben, nein?
JL Peyret
20

Ich verwende eine leicht modifizierte Version der Einstellungen von "if DEBUG", die Harper Shelby veröffentlicht hat. Abhängig von der Umgebung (win / linux / etc.) Muss der Code möglicherweise etwas angepasst werden.

Ich habe in der Vergangenheit das "if DEBUG" verwendet, aber ich stellte fest, dass ich gelegentlich Tests mit DEUBG durchführen musste, das auf False gesetzt war. Was ich wirklich unterscheiden wollte, wenn die Umgebung Produktion oder Entwicklung war, gab mir die Freiheit, das DEBUG-Level zu wählen.

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'

Ich würde diese Art der Einstellung immer noch als in Arbeit betrachten. Ich habe keine Möglichkeit gesehen, mit Django-Einstellungen umzugehen, die alle Grundlagen abdeckten, und war gleichzeitig kein totaler Aufwand beim Einrichten (ich bin mit den Methoden der 5x-Einstellungsdateien nicht fertig).

T. Stone
quelle
Dies ist die Art von Dingen, die Djangos Einstellungen als tatsächliche Codedatei erlauben, und ich habe darauf hingewiesen. Ich habe so etwas noch nicht selbst gemacht, aber es ist definitiv die Art von Lösung, die eine bessere allgemeine Antwort sein könnte als meine.
Harper Shelby
3
Ich bin gerade zum ersten Mal darauf gestoßen und habe mich (erfolgreich!) Für die Verwendung Ihrer Lösung entschieden, mit einem kleinen Unterschied: Ich habe uuid.getnode () verwendet, um die UUID meines Systems zu finden. Ich teste also, ob uuid.getnode () == 12345678901 (tatsächlich eine andere Nummer) anstelle des von Ihnen verwendeten os.environ-Tests. Ich konnte keine Dokumentation finden, die mich davon überzeugt, dass os.environ ['COMPUTERNAME'] pro Computer einzigartig ist.
Joe Golton
os.environ ['COMPUTERNAME'] funktioniert unter Amazon AWS Ubuntu nicht. Ich bekomme einen KeyError.
Nu Everest
Bei Verwendung der UUID hat sich diese Lösung für mich als die beste und einfachste erwiesen. Es erfordert nicht viel kompliziertes und übermodularisiertes Patchwork. In einer Produktionsumgebung müssen Sie Ihre Datenbankkennwörter und SECRET_KEY weiterhin in einer separaten Datei ablegen, die sich außerhalb der Versionskontrolle befindet.
Nu Everest
os.environ['COMPUTERNAME']funktioniert leider nicht auf PythonAnywhere. Sie erhalten einen KeyError.
Nbeuchat
14

Ich benutze eine settings_local.py und eine settings_production.py. Nachdem ich mehrere Optionen ausprobiert hatte, stellte ich fest, dass es einfach und schnell ist, Zeit mit komplexen Lösungen zu verschwenden, wenn sich einfach zwei Einstellungsdateien befinden.

Wenn Sie mod_python / mod_wsgi für Ihr Django-Projekt verwenden, müssen Sie es auf Ihre Einstellungsdatei verweisen. Wenn Sie auf Ihrem lokalen Server auf app / settings_local.py und auf Ihrem Produktionsserver auf app / settings_production.py verweisen, wird das Leben einfacher. Bearbeiten Sie einfach die entsprechende Einstellungsdatei und starten Sie den Server neu (der Django-Entwicklungsserver wird automatisch neu gestartet).

Kai
quelle
2
Und was ist mit dem lokalen Entwicklungsserver? Gibt es eine Möglichkeit, dem Django-Webserver (ausführen mit python manage.py runserver) mitzuteilen , welche Einstellungsdatei verwendet werden soll?
Akv
2
@akv Wenn Sie am Ende des Befehls runserver --settings = [Modulname] (keine Erweiterung .py) hinzufügen, können Sie angeben, welche Einstellungsdatei verwendet werden soll. Wenn Sie dies tun, tun Sie sich selbst einen Gefallen und erstellen Sie ein Shell-Skript / eine Batch-Datei mit den konfigurierten Entwicklungseinstellungen. Vertrau mir, deine Finger werden es dir danken.
T. Stone
Das ist die Lösung, die ich benutze. Das Hacken einer Einstellungsdatei, die sowohl für die Produktion als auch für die Entwicklung verwendet werden soll, ist chaotisch
George Godik
4
Ich denke, es ist besser, settings.py in der Entwicklung zu verwenden, da Sie es nicht immer angeben müssen.
Andre Bossard
Bin ich zu Recht davon ausgegangen, dass für diese Methode das Einstellungsmodul über den Proxy django.conf.settings importiert werden muss? Andernfalls müssen Sie Importdeklarationen bearbeiten, um beim Live-Push auf die richtige Einstellungsdatei zu verweisen.
Groady
8

TL; DR: Der Trick ist , zu ändern , os.environmentbevor Sie importieren settings/base.pyin jedem settings/<purpose>.pydieser stark die Dinge vereinfachen wird.


Wenn ich nur an all diese ineinander verschlungenen Dateien denke, bekomme ich Kopfschmerzen. Kombinieren, Importieren (manchmal bedingt), Überschreiben, Patchen von dem, was bereits festgelegt wurde, falls sich die DEBUGEinstellung später ändert. Was ein Alptraum!

Im Laufe der Jahre habe ich alle verschiedenen Lösungen durchlaufen. Sie funktionieren alle etwas , sind aber so schmerzhaft zu handhaben. WTF! Brauchen wir wirklich all diesen Ärger? Wir haben mit nur einer settings.pyDatei begonnen. Jetzt brauchen wir eine Dokumentation, um all dies in der richtigen Reihenfolge richtig zu kombinieren!

Ich hoffe, dass ich mit der folgenden Lösung endlich den (meinen) Sweet Spot erreicht habe.

Lassen Sie uns die Ziele zusammenfassen (einige häufig, andere meine)

  1. Geheimnisse geheim halten - nicht in einem Repo aufbewahren!

  2. Festlegen / Lesen von Schlüsseln und Geheimnissen über Umgebungseinstellungen im 12-Faktor-Stil .

  3. Haben Sie vernünftige Fallback-Standardeinstellungen. Ideal für die lokale Entwicklung benötigen Sie neben den Standardeinstellungen nichts mehr.

  4. … Aber versuchen Sie, die Standardeinstellung der Produktion sicher zu halten. Es ist besser, eine lokale Überschreibung von Einstellungen zu verpassen, als daran zu denken, die Standardeinstellungen sicher für die Produktion anzupassen.

  5. Sie können DEBUGauf eine Weise ein- und ausschalten, die sich auf andere Einstellungen auswirken kann (z. B. komprimiertes oder nicht komprimiertes Javascript).

  6. Das Umschalten zwischen Zweckeinstellungen wie lokal / Testen / Staging / Produktion sollte nur auf DJANGO_SETTINGS_MODULE, nicht mehr basieren .

  7. … Aber erlauben Sie eine weitere Parametrierung durch Umgebungseinstellungen wie DATABASE_URL.

  8. … Erlauben ihnen auch, verschiedene Zweckeinstellungen zu verwenden und sie lokal nebeneinander auszuführen, z. Produktions-Setup auf einem lokalen Entwicklercomputer, um auf die Produktionsdatenbank zuzugreifen oder komprimierte Stylesheets zu rauchen.

  9. Fehler, wenn eine Umgebungsvariable nicht explizit festgelegt ist (mindestens ein leerer Wert erforderlich), insbesondere in der Produktion, z. EMAIL_HOST_PASSWORD.

  10. Reagieren DJANGO_SETTINGS_MODULESie während des Startprojekts von django-admin auf die in manage.py festgelegte Standardeinstellung

  11. Halten Sie die Bedingungen auf ein Minimum. Wenn es sich bei der Bedingung um den beabsichtigten Umgebungstyp handelt (z. B. für die Produktionssatz-Protokolldatei und deren Drehung), überschreiben Sie die Einstellungen in der zugehörigen beabsichtigten Einstellungsdatei.

Nicht

  1. Lassen Sie django nicht die Einstellung DJANGO_SETTINGS_MODULE aus einer Datei lesen.
    Pfui! Überlegen Sie, wie meta das ist. Wenn Sie eine Datei (wie Docker Env) benötigen, lesen Sie diese in die Umgebung, bevor Sie einen Django-Prozess starten.

  2. Überschreiben Sie DJANGO_SETTINGS_MODULE nicht in Ihrem Projekt- / App-Code, z. basierend auf Hostname oder Prozessname.
    Wenn Sie faul sind, Umgebungsvariablen (wie für setup.py test) festzulegen, tun Sie dies in Tools, bevor Sie Ihren Projektcode ausführen.

  3. Vermeiden Sie Magie und Patches, wie Django seine Einstellungen liest, verarbeiten Sie die Einstellungen vor, aber stören Sie sie danach nicht.

  4. Kein komplizierter logischer Unsinn. Die Konfiguration sollte festgelegt und materialisiert werden und nicht im laufenden Betrieb berechnet werden. Das Bereitstellen von Fallback-Standardeinstellungen ist hier gerade genug Logik.
    Möchten Sie wirklich debuggen, warum haben Sie lokal die richtigen Einstellungen, aber in der Produktion auf einem Remote-Server, auf einem von hundert Computern, etwas anders berechnet? Oh! Unit Tests? Für Einstellungen? Ernsthaft?

Lösung

Meine Strategie besteht aus einer exzellenten Django-Umgebung, die mit iniStildateien verwendet wird und os.environmentStandardeinstellungen für die lokale Entwicklung bietet, sowie einigen minimalen und kurzen settings/<purpose>.pyDateien, die ein import settings/base.py AFTER the haben, os.environmentdas aus einer INIDatei festgelegt wurde. Dies gibt uns effektiv eine Art Einspritzung.

Der Trick hier besteht darin, os.environmentvor dem Import zu ändern settings/base.py.

Das vollständige Beispiel finden Sie im 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

Einstellungen / .env

Ein Standard für die lokale Entwicklung. Eine geheime Datei, um meistens die erforderlichen Umgebungsvariablen festzulegen. Setzen Sie sie auf leere Werte, wenn sie in der lokalen Entwicklung nicht benötigt werden. Wir geben hier settings/base.pyStandardeinstellungen an und versagen auf keinem anderen Computer, wenn diese in der Umgebung fehlen.

settings / local.py

Was hier passiert, ist das Laden der Umgebung von settings/.envund das Importieren allgemeiner Einstellungen von settings/base.py. Danach können wir einige überschreiben, um die lokale Entwicklung zu erleichtern.

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

Für die Produktion sollten wir keine Umgebungsdatei erwarten, aber es ist einfacher, eine zu haben, wenn wir etwas testen. Trotzdem werden nicht wenige Standardeinstellungen inline bereitgestellt, sodass settings/base.pyentsprechend reagiert werden kann.

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

Das Hauptinteresse hierbei ist DEBUGund ASSETS_DEBUGüberschreibt, sie werden os.environNUR auf die Python angewendet, wenn sie aus der Umgebung und der Datei fehlen.

Dies sind unsere Produktionsstandards, die nicht in die Umgebung oder Datei gestellt werden müssen, aber bei Bedarf überschrieben werden können. Ordentlich!

settings / base.py

Dies sind Ihre meist Vanille-Django-Einstellungen, mit ein paar Bedingungen und viel Lesen aus der Umgebung. Fast alles ist hier drin, um alle beabsichtigten Umgebungen konsistent und so ähnlich wie möglich zu halten.

Die Hauptunterschiede sind unten (ich hoffe, diese sind selbsterklärend):

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

Das letzte Bit zeigt die Kraft hier. ASSETS_DEBUGhat eine sinnvolle Standardeinstellung, die in überschrieben werden kann settings/production.pyund die sogar durch eine Umgebungseinstellung überschrieben werden kann! Yay!

Tatsächlich haben wir eine gemischte Hierarchie von Bedeutung:

  1. settings / .py - legt die Standardeinstellungen basierend auf dem Zweck fest und speichert keine Geheimnisse
  2. settings / base.py - wird hauptsächlich von der Umgebung gesteuert
  3. Prozessumgebungseinstellungen - 12-Faktor-Baby!
  4. settings / .env - lokale Standardeinstellungen für einen einfachen Start
Janusz Skonieczny
quelle
Hey Janusz ... also in der .env-Datei würden alle API-Schlüssel und Auth-Schlüssel und Passwörter usw. gehen? Genau wie TWILLIO_API = "abc123"? Oder TWILLIO_API = env ("TWILLIO_API")?
Dbinott
Ja, aber dies ist nur ein Fallback für Umgebungseinstellungen. Diese Datei ist praktisch für die Entwicklung, wird jedoch nicht im Repo gespeichert oder in die Produktion übertragen, wo Sie ausschließlich Umgebungseinstellungen oder ein Plattformäquivalent verwenden sollten, das wiederum die Umgebungseinstellungen für den Serverprozess festlegt.
Janusz Skonieczny
7

Ich verwalte meine Konfigurationen mit Hilfe von Django-Split-Einstellungen .

Es ist ein Drop-In-Ersatz für die Standardeinstellungen. Es ist einfach und doch konfigurierbar. Ein Refactoring Ihrer vorhandenen Einstellungen ist nicht erforderlich.

Hier ist ein kleines Beispiel (Datei 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()
    )

Das ist es.

Aktualisieren

Ich habe einen Blog-Beitrag über das Verwalten djangoder Einstellungen mit geschrieben django-split-sttings. Guck mal!

sobolevn
quelle
1
Ich habe versucht, dass .. stieß gegen eine Wand, als ich versuchte, meine Django-Unit-Tests durchzuführen .. Ich konnte einfach nicht herausfinden, wie ich angeben soll, aus welcher Einstellungsdatei ich lesen soll
Abbood
Ich habe einen Kern für Sie erstellt: gist.github.com/sobolevn/006c734f0520439a4b6c16891d65406c
sobolevn
Ich habe so etwas wie dies in meinem Code, so dass ich die settings.DEBUG Flag überprüfen , um zu wissen , wenn ich Sachen importieren Wanna .. das Flag immer auf false in django Unit - Tests eingestellt ist (siehe hier ) , so meine Arbeit um ist , sie außer Kraft setzt bei jeder Test wie so
Abbood
Hier ist jedoch eine andere Frage: Meine uwsgi.iniDatei hat unterschiedliche Einstellungen in dev / prod. Haben Sie eine Idee, wie Sie Werte aus meiner Einstellungsdatei auswählen können?
Abbood
Entschuldigung, ich verstehe das Setup nicht. Sie können eine separate Frage mit mehr Details stellen und ich werde versuchen, Ihnen zu helfen.
Sobolevn
6

Das Problem bei den meisten dieser Lösungen besteht darin, dass Sie entweder Ihre lokalen Einstellungen vor oder nach den allgemeinen Einstellungen angewendet haben.

Es ist also unmöglich, Dinge wie zu überschreiben

  • Die env-spezifischen Einstellungen definieren die Adressen für den zwischengespeicherten Pool. In der Haupteinstellungsdatei wird dieser Wert zum Konfigurieren des Cache-Backends verwendet
  • Die env-spezifischen Einstellungen fügen Apps / Middleware zur Standardeinstellung hinzu oder entfernen sie

gleichzeitig.

Eine Lösung kann mithilfe von Konfigurationsdateien im "ini" -Stil mit der ConfigParser-Klasse implementiert werden. Es unterstützt mehrere Dateien, Lazy String Interpolation, Standardwerte und viele andere Extras. Sobald eine Reihe von Dateien geladen wurden, können weitere Dateien geladen werden, und ihre Werte überschreiben gegebenenfalls die vorherigen.

Sie laden eine oder mehrere Konfigurationsdateien, abhängig von der Computeradresse, den Umgebungsvariablen und sogar den Werten in zuvor geladenen Konfigurationsdateien. Dann verwenden Sie einfach die analysierten Werte, um die Einstellungen zu füllen.

Eine Strategie, die ich erfolgreich angewendet habe, war:

  • Legen Sie eine defaults.iniStandarddatei
  • Überprüfen Sie den Computernamen, und laden Sie alle Dateien , die die umgekehrte FQDN angepasst, von der kürzesten Übereinstimmung mit dem längsten Match (so lud ich net.ini, dann net.domain.ini, dann net.domain.webserver01.ini, die jeweils möglicherweise Werte der vorherigen überschrieben). Dieses Konto gilt auch für Entwicklercomputer, sodass jeder seinen bevorzugten Datenbanktreiber usw. für die lokale Entwicklung einrichten kann
  • Überprüfen Sie, ob ein "Clustername" deklariert ist, und laden Sie in diesem Fall cluster.cluster_name.ini, um beispielsweise Datenbank- und Cache-IPs zu definieren

Als Beispiel für etwas, das Sie damit erreichen können, können Sie einen "Subdomain" -Wert pro Umgebung hostname: %(subdomain).whatever.netdefinieren , der dann in den Standardeinstellungen (as ) verwendet wird, um alle erforderlichen Hostnamen und Cookies zu definieren, die Django benötigt, um zu funktionieren.

Dies ist so trocken, wie ich es bekommen konnte. Die meisten (vorhandenen) Dateien hatten nur 3 oder 4 Einstellungen. Darüber hinaus musste ich die Kundenkonfiguration verwalten, sodass ein zusätzlicher Satz von Konfigurationsdateien (mit Dingen wie Datenbanknamen, Benutzern und Kennwörtern, zugewiesenen Subdomänen usw.) vorhanden war, einer oder mehrere pro Kunde.

Sie können dies so niedrig oder so hoch wie nötig skalieren. Sie geben einfach die Schlüssel in die Konfigurationsdatei ein, die Sie pro Umgebung konfigurieren möchten. Wenn eine neue Konfiguration erforderlich ist, setzen Sie den vorherigen Wert in die Standardkonfiguration und überschreiben Sie ihn wo nötig.

Dieses System hat sich als zuverlässig erwiesen und funktioniert gut mit der Versionskontrolle. Es wird seit langem verwendet, um zwei separate Anwendungscluster (15 oder mehr separate Instanzen der Django-Site pro Computer) mit mehr als 50 Kunden zu verwalten, bei denen die Größe und die Mitglieder der Cluster je nach Stimmung des Systemadministrators ihre Größe und Mitglieder wechselten. .

umgeschrieben
quelle
1
Haben Sie ein Beispiel dafür, wie Sie die Einstellungen vom INI in die Einstellungen von Django laden?
Kaleissin
Siehe docs.python.org/2/library/configparser.html . Sie können einen Parser mit laden, config = ConfigParser.ConfigParser() dann Ihre Dateien lesen config.read(array_of_filenames)und Werte mit abrufen config.get(section, option). Zuerst laden Sie Ihre Konfiguration und dann lesen Sie damit die Werte für die Einstellungen.
umgeschrieben
5

Ich arbeite auch mit Laravel und ich mag die Implementierung dort. Ich habe versucht, es nachzuahmen und mit der von T. Stone vorgeschlagenen Lösung zu kombinieren (siehe oben):

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

Vielleicht würde dir so etwas helfen.

Robert Kuzma
quelle
4

Denken Sie daran, dass settings.py eine Live-Codedatei ist. Angenommen, Sie haben DEBUG nicht für die Produktion eingestellt (was eine bewährte Methode ist), können Sie Folgendes tun:

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

Ziemlich einfach, aber theoretisch können Sie jede Komplexität erreichen, die nur auf dem Wert von DEBUG basiert - oder auf jeder anderen Variablen- oder Codeprüfung, die Sie verwenden möchten.

Harper Shelby
quelle
4

Für die meisten meiner Projekte verwende ich folgendes Muster:

  1. Erstellen Sie settings_base.py, in der ich Einstellungen speichere, die für alle Umgebungen gleich sind
  2. Immer wenn ich eine neue Umgebung mit bestimmten Anforderungen verwenden muss, erstelle ich eine neue Einstellungsdatei (z. B. settings_local.py), die den Inhalt von settings_base.py erbt und die richtigen Einstellungsvariablen überschreibt / hinzufügt ( from settings_base import *)

(Um manage.py mit einer benutzerdefinierten Einstellungsdatei auszuführen, verwenden Sie einfach die Befehlsoption --settings: manage.py <command> --settings=settings_you_wish_to_use.py )

dzida
quelle
3

Meine Lösung für dieses Problem ist auch eine Mischung aus einigen hier bereits genannten Lösungen:

  • Ich behalte eine Datei namens local_settings.py, die den Inhalt USING_LOCAL = Truein dev und USING_LOCAL = Falsein prod enthält
  • In settings.pyIch importiere diese Datei, um die USING_LOCALEinstellung zu erhalten

Ich stütze dann alle meine umgebungsabhängigen Einstellungen auf diese:

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

Ich ziehe es vor, zwei separate settings.py-Dateien zu haben, die ich pflegen muss, da ich meine Einstellungen einfacher in einer einzigen Datei strukturieren kann, als sie auf mehrere Dateien zu verteilen. Wenn ich eine Einstellung aktualisiere, vergesse ich nicht, dies für beide Umgebungen zu tun.

Natürlich hat jede Methode ihre Nachteile und diese ist keine Ausnahme. Das Problem hierbei ist, dass ich die local_settings.pyDatei nicht überschreiben kann, wenn ich meine Änderungen in die Produktion übertrage, was bedeutet, dass ich nicht einfach alle Dateien blind kopieren kann, aber damit kann ich leben.

Miguel Ventura
quelle
3

Ich benutze eine Variation von jpartogi, die ich etwas kürzer finde:

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)

Grundsätzlich habe ich auf jedem Computer (Entwicklung oder Produktion) die entsprechende Datei hostname_settings.py, die dynamisch geladen wird.

Stratosgear
quelle
3

Es gibt auch Django Classy Settings. Ich persönlich bin ein großer Fan davon. Es wurde von einer der aktivsten Personen im Django IRC gebaut. Sie würden Umgebungsvariablen verwenden, um Dinge festzulegen.

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

SudoKid
quelle
3

1 - Erstellen Sie einen neuen Ordner in Ihrer App und benennen Sie ihn.

2 - Erstellen Sie nun eine neue __init__.pyDatei und schreiben Sie hinein

from .base import *

try:
    from .local import *
except:
    pass

try:
    from .production import *
except:
    pass

3 - Erstellen Sie drei neue Dateien im Namen des Einstellungsordners local.pyund production.pyund base.py.

4 - base.pyKopieren Sie den gesamten Inhalt des vorherigen settings.pyOrdners und benennen Sie ihn beispielsweise in etwas anderes um old_settings.py.

5 - Ändern Sie in base.py Ihren BASE_DIR-Pfad so, dass er auf Ihren neuen Einstellungspfad verweist

Alter Pfad-> BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Neuer Weg -> BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

Auf diese Weise kann das Projektverzeichnis strukturiert und zwischen Produktion und lokaler Entwicklung verwaltet werden.

Jack Ryan
quelle
2

settingsErstellen Sie unterschiedliche Einstellungsdateien, um unterschiedliche Konfigurationen in unterschiedlichen Umgebungen zu verwenden . Starten Sie in Ihrem Bereitstellungsskript den Server mithilfe von --settings=<my-settings.py>Parametern, über die Sie unterschiedliche Einstellungen in unterschiedlichen Umgebungen verwenden können.

Vorteile dieses Ansatzes :

  1. Ihre Einstellungen sind je nach Umgebung modular

  2. Sie können die master_settings.pyKonfiguration mit der Basiskonfiguration in die importieren environmnet_configuration.pyund die Werte überschreiben, die Sie in dieser Umgebung ändern möchten.

  3. Wenn Sie ein großes Team haben, kann jeder Entwickler sein eigenes haben, local_settings.pydas er dem Code-Repository hinzufügen kann, ohne dass das Risiko besteht, die Serverkonfiguration zu ändern. Sie können diese lokalen Einstellungen hinzufügen , .gitnorewenn Sie verwenden git oder .hginorewenn Sie Mercurial für Versionskontrolle (oder irgendeine andere). Auf diese Weise sind lokale Einstellungen nicht einmal Teil der eigentlichen Codebasis, um sie sauber zu halten.

Moinuddin Quadri
quelle
2

Ich hatte meine Einstellungen wie folgt aufgeteilt

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

Wir haben 3 Umgebungen

  • dev
  • Inszenierung
  • Produktion

Jetzt sollten Inszenierung und Produktion offensichtlich die maximal mögliche ähnliche Umgebung haben. Also haben wir gehaltenprod.py für beide gehalten.

Aber es gab einen Fall, in dem ich feststellen musste, dass der laufende Server ein Produktionsserver ist. @T. Stones Antwort half mir, den Scheck wie folgt zu schreiben.

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
quelle
1

Ich differenziere es in manage.py und habe zwei separate Einstellungsdateien erstellt: local_settings.py und prod_settings.py.

In manage.py überprüfe ich, ob der Server ein lokaler Server oder ein Produktionsserver ist. Wenn es sich um einen lokalen Server handelt, wird local_settings.py geladen, und wenn es sich um einen Produktionsserver handelt, wird prod_settings.py geladen. Im Grunde würde es so aussehen:

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

Ich fand es einfacher, die Einstellungsdatei in zwei separate Dateien zu trennen, als viele ifs in der Einstellungsdatei durchzuführen.

Joshua Partogi
quelle
1

Als Alternative zum Verwalten einer anderen Datei, wenn Sie möchten: Wenn Sie git oder ein anderes VCS verwenden, um Codes von lokal auf den Server zu übertragen, können Sie die Einstellungsdatei zu .gitignore hinzufügen.

Auf diese Weise können Sie problemlos an beiden Stellen unterschiedliche Inhalte haben. Auf dem Server können Sie also eine unabhängige Version von settings.py konfigurieren. Änderungen, die am lokalen Server vorgenommen werden, wirken sich nicht auf den Server aus und umgekehrt.

Außerdem wird die Datei settings.py von github entfernt, der große Fehler, den ich bei vielen Neulingen gesehen habe.

sprksh
quelle
0

Ich denke, die beste Lösung wird von @T vorgeschlagen. Stone, aber ich weiß nicht, warum ich in Django nicht die DEBUG-Flagge benutze. Ich schreibe den folgenden Code für meine Website:

if DEBUG:
    from .local_settings import *

Immer sind die einfachen Lösungen besser als die komplexen.

seyedrezafar
quelle
-2

Ich fand die Antworten hier sehr hilfreich. (Wurde dies endgültiger gelöst? Die letzte Antwort war vor einem Jahr.) Nachdem ich alle aufgeführten Ansätze berücksichtigt hatte, fand ich eine Lösung, die ich hier nicht aufgelistet sah.

Meine Kriterien waren:

  • Alles sollte in der Quellcodeverwaltung sein. Ich mag keine fummeligen Teile, die herumliegen.
  • Im Idealfall behalten Sie die Einstellungen in einer Datei. Ich vergesse Dinge, wenn ich sie nicht richtig ansehe :)
  • Keine manuellen Änderungen zum Bereitstellen. Sollte in der Lage sein, mit einem einzigen Fabric-Befehl zu testen / zu pushen / bereitzustellen.
  • Vermeiden Sie es, Entwicklungseinstellungen in die Produktion zu verlieren.
  • Halten Sie sich so nah wie möglich an das "Standard" -Django-Layout (* Husten *).

Ich dachte, das Einschalten des Host-Computers wäre sinnvoll, aber dann stellte ich fest, dass das eigentliche Problem hier unterschiedliche Einstellungen für unterschiedliche Umgebungen sind , und hatte einen Aha-Moment. Ich habe diesen Code am Ende meiner Datei settings.py eingefügt:

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'

Auf diese Weise verwendet die App standardmäßig die Produktionseinstellungen. Dies bedeutet, dass Sie Ihre Entwicklungsumgebung explizit auf die Whitelist setzen. Es ist viel sicherer zu vergessen, die Umgebungsvariable lokal festzulegen, als wenn es umgekehrt wäre und Sie vergessen haben, etwas in der Produktion festzulegen und einige Entwicklungseinstellungen verwenden zu lassen.

Bei der lokalen Entwicklung entweder über die Shell oder in einem .bash_profile oder wo auch immer:

$ export DJANGO_DEVELOPMENT_SERVER=yep

(Oder wenn Sie unter Windows entwickeln, legen Sie dies über die Systemsteuerung oder wie auch immer es heutzutage heißt ... Windows hat es immer so dunkel gemacht, dass Sie Umgebungsvariablen festlegen können.)

Bei diesem Ansatz befinden sich die Entwicklungseinstellungen alle an einem (Standard-) Ort und überschreiben bei Bedarf einfach die Produktionseinstellungen. Jegliches Herumspielen mit Entwicklungseinstellungen sollte absolut sicher sein, sich der Quellcodeverwaltung zu widmen, ohne die Produktion zu beeinträchtigen.

Jason Boyd
quelle
Es ist besser, nur verschiedene Konfigurationsdateien zu verwalten und mit der DJango-Standard-Env-Variablen DJANGO_SETTINGS_MODULE
Rob Grant