Django Lokale Einstellungen

75

Ich versuche, local_setting in Django 1.2 zu verwenden , aber es funktioniert bei mir nicht. Im Moment füge ich meinem Projekt nur local_settings.py hinzu .

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'banco1',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '123',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

local_settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'banco2',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '123',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

Das Problem ist , dass local_settings.py nicht enthebt settings.py . Was ist falsch?

Michel Andrade
quelle
2
Im Moment, in dem ich diese Frage gelesen habe, gibt es drei sehr unterschiedliche und alle interessanten Antworten. Ich war verwirrt über die Kommentare zu Daniels Lösung. Es sieht für mich so aus, als gäbe es keine Komplettlösung. Daniels Lösung ist einfach und effizient. Die Lösung von jano ist irgendwie sauberer, was nur in bereits sauberen Umgebungen zu geringen Kosten Sinn macht. Johns Lösung ist irgendwie schwerer, aber in den engsten Umgebungen interessant. Alles in allem wählen Sie einfach die einfachste aus, die zu Ihrem Anwendungsfall passt.
Stéphane Gourichon

Antworten:

138

Sie können local_settings.py nicht einfach hinzufügen, sondern müssen es explizit importieren.

Fügen Sie am Ende Ihrer settings.py Folgendes hinzu:

try:
    from local_settings import *
except ImportError:
    pass

Der Try / Except-Block ist vorhanden, sodass Python den Fall einfach ignoriert, wenn Sie keine local_settings-Datei definiert haben.

Daniel Roseman
quelle
3
Ja, so grep -r "local_setting" djangoziemlich garantiert, dass es kein "out of the box" -Verhalten ist
Yuji 'Tomita' Tomita
8
Dieser Ansatz bedeutet, dass an JEDEM Speicherort nicht versionierter Code ausgeführt wird. Dies ist ein Anti-Pattern.
Pydanny
2
@pydanny Welche Alternative schlagen Sie vor?
Brodney
1
@pydanny - diese Antwort sagt explizit: local_settings.py (or more commonly prod_settings.py) is NOT in version control, and used in production by specifying --settings=prod_settings or similar.Es scheint also immer noch, als würde nicht versionierter Code an jedem Ort ausgeführt. Ist das nicht der Fall?
Joseph
1
Es ist eine sehr schlechte Idee, die Ausnahme weiterzugeben. Wenn local_settings.py lediglich einen Syntaxfehler aufweist, wird dieser nicht importiert und Sie können die Standardeinstellungen von settings.py ausführen, die Sie nicht erwarten. Zum Beispiel, wenn settings.py eine Verbindung zu einer Produktionsdatenbank herstellt. Es ist besser, immer eine local_settings.py zu benötigen und einen Fehler auszugeben, wenn er nicht gefunden wird.
Matt
81

Dies ist die beste Vorgehensweise, die ich denke:

  • local_settings Importe aus settings
  • local_settingsÜberschreibungen spezifische Einstellungen für das lokale Umfeld, vor allem DATABASES, SECRET_KEY, ALLOWED_HOSTSund DEBUGVariablen
  • Übergabe an Django Management befiehlt die Flagge --settings=local_settings

Sie könnten so implementieren local_settings:

from settings import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'banco2',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '123',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

Einige zusätzliche wichtige Punkte:

  • settings.py befindet sich in der Versionskontrolle und ist so geschrieben, dass es von Mitwirkenden verwendet werden kann
  • local_settings.py(oder häufiger prod_settings.py) befindet sich NICHT in der Versionskontrolle und wird in der Produktion durch Angabe --settings=prod_settingsoder Ähnliches verwendet.

Wenn Sie die Bestandseinstellungsdatei so wenig wie möglich berühren, können Sie auch Ihre Django-Version leichter aktualisieren. Wenn Sie Django auf die nächste Version aktualisieren, überprüfen Sie den Unterschied in Ihrem settings.pyund Ihrem Bestand und ergreifen Sie die erforderlichen Maßnahmen, je nachdem, was sich geändert hat. Änderungen an den Standardwerten können wichtig settings.pysein. Je weniger Sie die Originaldatei berührt haben , desto einfacher ist es, vorgelagerte Änderungen zu erkennen.

Janos
quelle
2
Ich mag diesen Ansatz. Wenn Sie die leere Einstellungsdatei so wenig wie möglich berühren, können Sie neue Einstellungen beim Upgrade Ihrer Django-Version einfacher konfigurieren.
m000
3
Dies sollte die akzeptierte Antwort sein. Lesen Sie die Kommentare in der akzeptierten Antwort aus Gründen, warum diese problematisch ist ("nicht versionierter Code, der an JEDEM Speicherort ausgeführt wird").
Eierbeine
6
Ich kann nicht sehen, wie sich dies von der akzeptierten Antwort unterscheidet. Diese local_settings sollten ebenfalls nicht versioniert werden. In diesem Fall können Entwickler möglicherweise local_settings auf der Produktionsmaschine versehentlich ändern.
GergelyPolonkai
Das einzige was mich stört ist das import *. stackoverflow.com/questions/2386714/why-is-import-bad
azmeuk
1
@azmeuk Der Kontext ist wichtig. import *ist im Allgemeinen schlecht, aber durchaus akzeptabel, wenn es der einzige Import in eine Datei ist. Und es sollte der Import inlocal_settings.py
Janos
11

Da das Thema routinemäßig wieder auftaucht, möchte ich zusammenfassen, warum Sie diesen Ansatz in Betracht ziehen sollten:

  • Eine dumme Einstellungsdatei ist sehr schnell und einfach zu ändern. vor allem in einer Produktionsumgebung. Kein Python erforderlich: Jeder Idiot kann das Datenbankkennwort in einer Datei ändern, in der nur Namen und Werte aufgelistet sind. vor allem im Vergleich zu einer komplexen Python-Einstellungsdatei voller mysteriöser gefährlicher BIGCAPS-Namen.

  • Die Anwendung settingssollte vollständig von der Anwendung getrennt sein code. Sie können eine config.ini außerhalb des Repository-Stammverzeichnisses ablegen und sich nie wieder Sorgen machen, dass ein Repo-Pull Ihre Einstellungen oder Ihre persönlichen Einstellungen, die das Repo verschmutzen, oder dieser clevere Code in Ihren settings.py nicht zum Vorteil aller anderen in das Repo schafft .

Dies gilt nicht für kleine Projekte, aber bei größeren Projekten bin ich zu dem Schluss gekommen, dass die Strategie local_settings es einfach nicht schafft. Mit der Zeit schleicht sich genug Anwendungsprogrammierung ein, so dass es schwierig wird, damit umzugehen. in erster Linie, wenn Einstellungen abgeleitet und / oder codependent werden. Es kann gute Gründe dafür geben, dass die Einstellungen entsprechend den lokalen Einstellungen reagieren, wodurch der Import einer local_settingsDatei in die Mitte gezwungen wird settings.py. Ich finde, die Dinge werden langsam chaotisch, wenn das passiert.

Meine aktuelle Lösung besteht darin, eine configDatei zu verwenden. Ich nenne sie "local.ini". Es enthält nur die Werte, die sich zwischen den bereitgestellten Instanzen tatsächlich ändern. Es gibt keinen Code: Sie sind nur Werte und Boolesche Werte:

[global]
domain = 127.0.0.1:8000
database_host = 127.0.0.1
database_name = test_database
debug = Yes
google_analytics_id = UA-DEV-1
payments = testing
use_cdn = No

Mit dieser Funktion kann ich das Gleiche settings.pywie jeden anderen Anwendungscode behandeln: Optimieren, Einchecken und Bereitstellen, ohne mir Gedanken über das Testen des Codes machen zu müssen, der möglicherweise in einem Python-Code von local_settings lauert. Mein settings.pyist frei von Rennbedingungen, die auftreten, wenn spätere Einstellungen von lokalen Einstellungen abhängen, und ich kann Funktionen ein- und ausschalten, indem ich einfach zu befolgenden linearen Code schreibe. Keine Eile mehr zwicken die local_settings Datei , wenn ich vergessen habe einigen neuen Mehrwert zu schaffen , und nicht mehr daves_local_settings.pyund bobs_local_settings.pyDateien in das Repository kriechen.

from ConfigParser import RawConfigParser
parser = RawConfigParser()

APPLICATION_ROOT = path.abspath(path.dirname(__file__))
parser.readfp(open(path.join(APPLICATION_ROOT, 'local.ini')))

# simple variables
DATABASE_HOST = parser.get('global', 'database_host')
DATABASE_NAME = parser.get('global', 'database_name')

# interdependencies
from version import get_cdn_version
CDN = 'd99phdomw5k72k.cloudfront.net'
if parser.getboolean('global', 'use_cdn'):
    STATIC_URL = '/{}/static/{}/'.format(CDN, get_cdn_version())
else:
    STATIC_URL = '/static/'


# switches
payments = parser.get('global', 'payments')
if payments == 'testing':
    PAYMENT_GATEWAY_ENDPOINT = 'https://api.sandbox.gateway.com'
else:
    PAYMENT_GATEWAY_ENDPOINT = 'https://api.live.gateway.com'

Wenn Sie auf ein BOFH stoßen , wie ich es einmal getan habe, war er besonders begeistert von der Möglichkeit, das local.inials /etcVerzeichnis in das Verzeichnis zu stecken /etc/ourapp.iniund so das Anwendungsverzeichnis selbst als reinen Repository-Export zu halten. Sicher, Sie könnten das mit einer local_settings.py machen, aber das Letzte, was er tun wollte, war, sich mit Python-Code herumzuschlagen. Eine einfache Konfigurationsdatei, mit der er umgehen konnte.

John Mee
quelle
Warum möchten Sie eine andere Syntax verwenden (und einen Parser benötigen) als eine normale Python-Datei? Deshalb haben wir Namespace.
Joost
Weil a regular Python fileausgeführt wird, während eine Konfigurationsdatei eine sehr einfache Sammlung dummer Einstellungen ist; Es gibt keine Versuchung, es dazu zu bringen, "kluge" Dinge zu tun, und jeder Idiot (z. B. Nicht-Python-Leute oder Sie selbst 12 Monate später) kann herausfinden, wie die Grundeinstellungen aktualisiert werden können, ohne befürchten zu müssen, alles zu beschädigen.
John Mee
Die Syntax ist jedoch praktisch identisch. Ich denke, es ist eine Frage der Präferenz.
Joost
1
Wie gesagt, kleine Projekte zahlen sich wenig aus. Sobald Ihre settings.py jedoch über 50 Zeilen hinaus wächst oder die import local_settings.pynicht mehr ganz unten steht, sehen Sie mehr Wert.
John Mee
Für mich ist das Schlimmste daran, dass Sie für jeden einzelnen Einstellungswert Code schreiben müssen. Und wenn Sie einfach alles in die Datei importieren, müssen Sie die Variablen aus Stings umwandeln, wenn es sich um etwas anderes handelt. Außerdem verkette ich gerne Dinge. Ich habe auch viele Listen und Wörterbücher in meiner Konfiguration.
Kagronick
9

Ich habe eine Kopie von __local_settings.py:

  • local_settings.py wird in der Versionskontrolle ignoriert, aber nicht __local_settings.py
  • Update README.md, um das Team über die Einrichtung zu informieren: cp {__,}local_settings.py(die eine Kopie für ihre local_settings erstellen)

In der Vergangenheit

Ich habe diese Einstellungen importiert.

# settings.py
DATABASE = {...}

try:
    from .local_settings import *
except ImportError:
    pass

jetzt

Ich importiere einfach die Einstellungen selbst aus dem local_settings.py.

Und mit folgendem Befehl : python manage.py runserver --settings=<proj>.local_settings.

# local_settings.py & __local_settings.py
from .settings import *

DATABASE = {...}

Und da interagiere ich normalerweise nicht manage.pydirekt mit, weil einige Parameter für mich explizit notwendig sind (zB address:port). Deshalb habe ich all diese Befehle in meine Makefile.

Hier ist zum Beispiel mein Makefile:

run:
    python manage.py runserver 0.0.0.0:8000 --settings=<proj>.local_settings

sh:
    python manage.py shell_plus --settings=<proj>.local_settings

dep:
    npm install
    pip install -r requirements.txt

So:

make dep
make sh 
make run

Fazit

Vorausgesetzt , dass Sie nicht verwenden , Makefilewie Sie Ihren Workflow, dann können Sie die frühere Methode verwenden, aber wenn Sie Make - Datei verwenden, dann glaube ich , es besser ist , in Ihrem Makefile deutlicher zu sein.

Yeo
quelle
Makefile ist nur eine Idee und Meinung. Versuchen Sie, den früheren Ansatz zu verwenden, kann im Allgemeinen besser sein
Yeo
6

Vor dem Ausführen des Servers tun

export DJANGO_SETTINGS_MODULE=your_app_name.local_settings Dabei sollte Ihr_App_Name durch den Namen Ihrer App ersetzt werden. Und vergiss nicht zu tun

from settings import *

in Ihrer Datei local_settings.py

Siddhesh Suthar
quelle
2
Sie sollten die .py aus der Zeile weglassen (und nicht vergessen, den App-Namen durch Ihre eigentliche Anwendung zu ersetzen)
johanvdw
4

Ein weiterer Ansatz besteht darin, python-dotenvUmgebungsvariablen zu verwenden, um Einstellungen für verschiedene Umgebungen anzupassen.

Erstellen Sie die .envDatei neben Ihrem settings.py:

# .env
SECRET_KEY=your-secret-key
DATABASE_PASSWORD=your-database-password

Fügen Sie Ihrem Code den folgenden Code hinzu settings.py:

# settings.py
from dotenv import load_dotenv
load_dotenv()

# OR, explicitly providing path to '.env'
from pathlib import Path  # python 3.4+
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)

Zu diesem Zeitpunkt sind analysierte Schlüssel / Werte aus der .envDatei als Umgebungsvariablen vorhanden und können bequem über Folgendes aufgerufen werden os.getenv():

# settings.py
import os
SECRET_KEY = os.getenv('SECRET_KEY')
DATABASE_PASSWORD = os.getenv('DATABASE_PASSWORD')   
Eugene Yarmash
quelle
1

Ich habe die ähnliche Lösung gefunden. Dies ist meine Konfiguration für diesen Fall:

settings.py:

DEBUG = False

try:
    from local_settings import *

except ImportError:
    pass

if DEBUG is False:
    ALLOWED_HOSTS = ['sth.com']
    DATABASES = {
        ....
    }

local_settings.py:

from settings import *
ALLOWED_HOSTS = ['*']
DEBUG = True
DATABASES = {
    ...
}
Darex1991
quelle
1

Fügen Sie dies am Ende der Datei settings.py hinzu

try:
    from .local_settings import *
except ImportError:
    pass

Erstellen Sie beispielsweise die Datei local_settings.py mit Ihren neuen Einstellungen

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'banco2',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '123',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}
Holovin
quelle