350 Stimmen

Wie verwaltet man lokale vs. Produktionseinstellungen in Django?

Wie sollten die Einstellungen für die lokale Entwicklung und den Produktionsserver gehandhabt werden? Einige von ihnen (wie z. B. Konstanten usw.) können in beiden geändert/zugelassen 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 zu 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... :(

Edit: Da es anscheinend keine Standardantwort auf diese Frage gibt, habe ich die am weitesten verbreitete Methode übernommen.

331voto

Zwei Kugeln Django: Bewährte Praktiken für Django 1.5 empfiehlt die Verwendung einer Versionskontrolle für Ihre Einstellungsdateien und die Speicherung der Dateien in einem separaten Verzeichnis:

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

En base.py Datei enthält allgemeine Einstellungen (wie MEDIA_ROOT oder ADMIN), während local.py y production.py haben standortspezifische Einstellungen:

In der Basisdatei settings/base.py :

INSTALLED_APPS = (
    # common apps...
)

In der Datei mit den lokalen Entwicklungseinstellungen settings/local.py :

from project.settings.base import *

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

In der Datei Produktionseinstellungen settings/production.py :

from project.settings.base import *

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

Wenn Sie dann django starten, fügen Sie die --settings Option:

# 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 eine Muster-Vorlage für das Projektlayout auf Github.

136voto

ohnoes Punkte 5322

En settings.py :

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

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

76voto

MiniQuark Punkte 43252

Anstelle von settings.py verwenden Sie dieses Layout:

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

common.py ist der Ort, an dem sich der Großteil Ihrer Konfiguration befindet.

prod.py importiert alles von common und überschreibt alles, was überschrieben werden muss:

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

# Production overrides
DEBUG = False
#...

Ähnlich, dev.py importiert alles von common.py und überschreibt alles, was es überschreiben muss.

Endlich, __init__.py ist der Ort, an dem Sie entscheiden, welche Einstellungen geladen werden sollen, und an dem Sie auch 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 enthalten, außer Geheimnisse
  2. Die meisten Konfigurationen befinden sich an einem Ort: common.py .
  3. Produktspezifische Dinge gehen in prod.py , dev-spezifische Dinge gehen in dev.py . Es ist ganz einfach.
  4. Sie können Dinge aus common.py en prod.py o dev.py und Sie können alles überschreiben, was in __init__.py .
  5. Das ist ganz einfaches Python. Keine Re-Import-Hacks.

20voto

T. Stone Punkte 18736

Ich verwende eine leicht modifizierte Version der "if DEBUG"-Einstellungen, die Harper Shelby veröffentlicht hat. Offensichtlich abhängig von der Umgebung (win/linux/etc.) der Code möglicherweise ein bisschen gezwickt werden müssen.

In der Vergangenheit habe ich die Option "if DEBUG" verwendet, aber ich habe festgestellt, dass ich gelegentlich Tests durchführen musste, bei denen DEUBG auf False gesetzt war. Ich wollte wirklich unterscheiden, ob es sich um eine Produktions- oder Entwicklungsumgebung handelt, was mir die Freiheit gab, den 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 von Einstellungen immer noch als unausgereift betrachten. Ich habe noch keine Möglichkeit gesehen, Django-Einstellungen zu handhaben, die alle Grundlagen abdeckt und gleichzeitig nicht total umständlich einzurichten ist (ich bin nicht mit den 5x-Einstellungsdateien-Methoden zufrieden).

16voto

Janusz Skonieczny Punkte 15373

TL;DR: Der Trick besteht darin, die os.environment bevor Sie importieren settings/base.py in jedem settings/<purpose>.py Dies wird die Arbeit erheblich vereinfachen.


Allein der Gedanke an all diese ineinandergreifenden Akten bereitet mir Kopfschmerzen. Kombinieren, importieren (manchmal bedingt), überschreiben, Parcheando von dem, was bereits für den Fall gesetzt wurde DEBUG Einstellung später geändert werden. Was für ein Alptraum!

Im Laufe der Jahre habe ich alle möglichen Lösungen ausprobiert. Sie alle etwas funktionieren, aber so mühsam zu verwalten sind. WTF! Brauchen wir wirklich diesen ganzen Ärger? Wir haben mit nur einer settings.py Datei. Jetzt brauchen wir eine Dokumentation, um all diese Dateien in der richtigen Reihenfolge zu kombinieren!

Ich hoffe, dass ich mit der nachstehenden Lösung endlich den (meinen) Nerv getroffen habe.

Rekapitulieren wir die Ziele (einige gemeinsame, einige meine)

  1. Behalten Sie Geheimnisse für sich - bewahren Sie sie nicht in einem Repo auf!

  2. Setzen/Lesen von Schlüsseln und Geheimnissen durch Umgebungseinstellungen, 12-Faktor-Stil .

  3. Vernünftige Standardwerte für den Fall der Fälle haben. Im Idealfall brauchen Sie für die lokale Entwicklung außer den Standardeinstellungen nichts weiter.

  4. aber versuchen Sie, die Standardeinstellungen produktionssicher zu halten. Es ist besser, eine Einstellung zu verpassen, die lokal überschrieben wird, als daran denken zu müssen, die Standardeinstellungen für die Produktion anzupassen.

  5. die Möglichkeit haben, zu wechseln DEBUG ein/aus in einer Weise, die sich auf andere Einstellungen auswirken kann (z. B. Verwendung von komprimiertem oder nicht komprimiertem Javascript).

  6. Der Wechsel zwischen den Zweckeinstellungen, wie z. B. lokal/Testing/Taging/Production, sollte nur auf der Grundlage DJANGO_SETTINGS_MODULE , nichts weiter.

  7. erlauben aber eine weitere Parametrisierung durch Umgebungseinstellungen wie DATABASE_URL .

  8. erlauben es ihnen auch, verschiedene Einstellungen zu verwenden und sie lokal nebeneinander laufen zu lassen, z.B. Produktions-Setup auf lokalem Entwickler-Rechner, um auf die Produktionsdatenbank zuzugreifen oder komprimierte Stylesheets zu testen.

  9. Schlägt fehl, wenn eine Umgebungsvariable nicht explizit gesetzt ist (erfordert mindestens einen leeren Wert), insbesondere in der Produktion, z. B. EMAIL_HOST_PASSWORD .

  10. Reagieren Sie auf den Standard DJANGO_SETTINGS_MODULE die in manage.py während django-admin startproject

  11. Beschränken Sie Konditionale auf ein Minimum, wenn die Bedingung die (z. B. für die Produktion eingestellte Protokolldatei und ihre Rotation), die Einstellungen in der zugehörigen Einstellungsdatei außer Kraft setzen.

Do not's

  1. Lassen Sie django keine DJANGO_SETTINGS_MODULE-Einstellungen aus einer Datei lesen.
    Igitt! Denken Sie daran, wie meta das ist. Wenn Sie eine Datei benötigen (wie Docker env), lesen Sie diese in die Umgebung ein, bevor Sie einen Django-Prozess starten.

  2. Überschreiben Sie DJANGO_SETTINGS_MODULE nicht in Ihrem Projekt/Anwendungscode, z.B. basierend auf dem Hostnamen oder Prozessnamen.
    Wenn Sie zu faul sind, eine Umgebungsvariable zu setzen (wie bei setup.py test ) tun Sie dies im Tooling, kurz bevor Sie Ihren Projektcode ausführen.

  3. Vermeiden Sie Magie und Parcheando, wie django seine Einstellungen liest, verarbeiten Sie die Einstellungen vor, aber mischen Sie sich danach nicht ein.

  4. Kein komplizierter, auf Logik basierender Unsinn. Die Konfiguration sollte fixiert und materialisiert sein und nicht on the fly berechnet werden. Die Bereitstellung einer Fallback-Standardwerte ist gerade genug Logik hier.
    Wollen Sie wirklich debuggen, warum Sie lokal die richtigen Einstellungen haben, aber in der Produktion auf einem entfernten Server, auf einem von hundert Rechnern, etwas anders berechnet wird? Oh! Unit-Tests? Für Einstellungen? Ernsthaft?

Lösung

Meine Strategie besteht aus hervorragenden django-umgebung verwendet mit ini Stil-Dateien, Bereitstellung von os.environment Vorgaben für die lokale Entwicklung, einige minimale und kurze settings/<purpose>.py Dateien, die ein import settings/base.py NACH les os.environment wurde von einer INI Datei. Dadurch erhalten wir eine Art von Einstellungsinjektion.

Der Trick dabei ist, dass man die os.environment bevor Sie importieren settings/base.py .

Um das vollständige Beispiel zu sehen, besuchen Sie das 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

Eine Vorgabe für die lokale Entwicklung. Eine geheime Datei, um meist benötigte Umgebungsvariablen zu setzen. Setzen Sie sie auf leere Werte, wenn sie für die lokale Entwicklung nicht benötigt werden. Wir bieten hier Standardwerte an und nicht in settings/base.py auf jedem anderen Rechner fehlschlagen, wenn sie in der Umgebung fehlen.

einstellungen/local.py

Was hier passiert, ist das Laden der Umgebung von settings/.env und dann die gemeinsamen Einstellungen importieren von settings/base.py . Danach können wir einige davon außer Kraft setzen, 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

Einstellungen/Produktion.py

Für die Produktion sollten wir keine Umgebungsdatei erwarten, aber es ist einfacher, eine zu haben, wenn wir etwas testen wollen. Wie auch immer, wir sollten ein paar Standardwerte inline bereitstellen, also settings/base.py entsprechend reagieren können.

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

Die wichtigsten Punkte, die hier von Interesse sind, sind DEBUG y ASSETS_DEBUG übersteuert, werden sie auf die Python os.environ NUR dann, wenn sie in der Umgebung und in der Datei nicht vorhanden sind.

Dies sind die Standardeinstellungen für die Produktion. Sie müssen nicht in die Umgebung oder Datei aufgenommen werden, können aber bei Bedarf überschrieben werden. Toll!

einstellungen/base.py

Dies sind Ihre meist vanilla django Einstellungen, mit ein paar Conditionals und viel von Lesen Sie sie aus der Umgebung. Fast alles ist hier drin, um alle Umgebungen konsistent und so ähnlich wie möglich zu halten.

Die wichtigsten Unterschiede sind nachstehend aufgeführt (ich hoffe, sie 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
# Ths 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 Stück zeigt, welche Kraft hier steckt. ASSETS_DEBUG hat eine sinnvolle Voreinstellung, die in den folgenden Abschnitten überschrieben werden kann settings/production.py und selbst das kann durch eine Umgebungseinstellung außer Kraft gesetzt werden! Juhu!

In der Tat haben wir eine gemischte Hierarchie der Bedeutung:

  1. settings/.py - setzt Standardwerte je nach Zweck, speichert keine Geheimnisse
  2. settings/base.py - wird hauptsächlich von der Umgebung kontrolliert
  3. Einstellungen der Prozessumgebung - 12-Faktor-Baby!
  4. settings/.env - lokale Standardeinstellungen für einen einfachen Start

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X