Settings organisieren
**********************
Normalerweise haben wir mehrere Umgebungen, in denen das Projekt (später) betrieben wird: die lokale Umgebung, eine Staging-Umgebung, Produktion usw. Jede Umgebung kann ihre eigenen spezifischen Einstellungen haben (zum Beispiel: DEBUG = True, ausführlichere Protokollierung, zusätzliche Apps usw.) . Um das zu ermöglichen, brauchen wir eine bessere Organisation der Settings.
Zusätzlich haben wir noch verschiedene Passwörter für Datenbanken, Secret-Keys und
.. admonition:: 12 Factors
12 Factors ist eine Sammlung von Empfehlungen zum Erstellen verteilter Web-Apps, die in der Cloud einfach bereitzustellen und zu skalieren sind.
Es behandelt neben Themen wie Codebase, Dependencies, Logs, auch das Thema Configuration. Eine der Hauptregeln ist, dass Konfiguration im Environment zu liegen hat, um Konfiguration von Code zu trennen. Passwörter werden also nicht hardcodiert in die Settings geschrieben, sondern als Umgebungsvariable abgelegt.
mehr Infos dazu hier: ``_
.. warning:: Sensible Daten
Sensitive Daten dürfen auf gar keinen Fall in die Versionskontrolle.
Diese Dateien müssen zwingend in ``.gitignore`` eingetragen werden, damit die Versionskontrolle sie ignoriert.
Auslagern von sensitiven Daten in Umgebungsvariablen
=====================================================
Wir gehen dabei den Weg über Environment-Variablen und das Modul django-environ. Wir könnten auch über ``os.environ`` direkt gehen, aber das ist nicht so komfortabel wie das Modul, zb. wenn es zu Key-Errorn kommt.
django-environ
----------------------
django-environ is the Python package that allows you to use Twelve-factor methodology to configure your Django application with environment variables.
Wir können dann eine ``.env``-Datei erstellen, die von ``django-environ`` gelesen werden kann. Wir müssen die Umgebungsvariablen also NICHT im Betriebsystem ablegen.
Mehr zu Django Environ: ``_
Wir installieren also:
...........................
.. code-block:: bash
pip install django-environ
.env Datei
...........................
Wir legen die Datei ``event_manager/.env`` an (also im Projektroot) und füllen sie mit folgendem Inhalt:
.. code-block:: bash
DEBUG=on
SECRET_KEY=2930923943209jalsdf023092384020239840
ALLOWED_HOSTS=127.0.0.1 localhost
In diese Datei kommen alle sensitiven Daten der aktuellen Umgebung. Passwörter für die Datenbank, Secret-Key, Port-Angaben und so weiter. Diese Datei wird nicht versioniert, dh. jeder, der das Repository importiert, benötigt ebenfalls wieder seine eigene ``.env``-Datei. Damit man weiß, was in dieser Datei drinzustehen hat, bietet es sich an, eine ``env_example`` mit dummy-Inhalt anzulegen.
An dieser Stelle noch ein Link zum Generieren eines Secret Keys: ``_
environ importieren
...........................
Wir importieren das Modul in den Settings und weisen den Konfigurationskonstranten die Werte zu:
.. code-block:: python
import environ
env = environ.Env(
DEBUG=(bool, False)
SETTINGS_MODULE=(str, "event_manager.settings.dev")
)
environ.Env.read_env(BASE_DIR / ".env")
DEBUG = env("DEBUG")
SECRET_KEY = env("SECRET_KEY")
ALLOWED_HOSTS = env('ALLOWED_HOSTS').split()
Wir weisen hier der DEBUG-Konstante einen Default-Wert zu, nämlich ``False``.
Sollte sich diese KONSTANTE nicht in der ``.env-Datei`` befinden, wird der
Defaultwert genommen. Sollte kein Defaultwert angegeben worden sein, gibt es
einen Key-Error, sollte sich die KONSTANTE nicht in .env befinden.
Wir belegen auch noch ``SETTINGS_MODULE`` mit einem Defaultwert.
.env ignorieren
...........................
Wir fügen ``.env`` in die ``.gitignore``-Datei ein, damit diese Datei nicht versioniert wird und Passwörter am Ende in einem frei zugänglichen Repository landen.
Wenn wir jetzt den ``runserver`` starten, sollte das Projekt fehlerfrei laufen.
Gehen wir also jetzt zum nächsten Schritt.
Settings-Datei für jede Umgebung
=====================================================
Jede Umgebung, in der das Projekt betrieben wird, hat andere Einstellung: eine andere Datenbank, ein anderer Logger usw. Im Produktivbetrieb sollte zum Beispiel die django-Debugbar nicht in den ``MIDDLEWARE`` stehen. Die ``TEMPLATES``-Liste hingegen wird u.U. auch im Produktivbetrieb die gleiche sein, wie im lokalen Betrieb.
Wir sehen also: es gibt Konfigurationen in den ``settings.py``, die für alle Umgebungen gelten, und manche nur für spezielle.
Unsere Gliederung der Settings wird also so sein, dass es eine Base-Settings gibt, von der alle anderen Settings erben und die überschreiben oder anreichern kann.
Settings organisieren
----------------------
ein Verzeichnis für die Settings
..................................
Wir legen ein neues Verzeichnis an: ``event_manager/event_manager/settings``, in welches die Settings für die verschiedenen Umgebungen gespeichert werden.
Die alte ``settings.py`` nennen wir um in ``settings_old.py``, da wir diese nicht mehr benötigen.
Settings-Dateien
..................................
In das ``event_manager/event_manager/settings``-Verzeichnis speichern wir zwei neue Dateien: ``base.py`` und ``dev.py``. Wenn später noch eine weitere Umgebung dazukommt, kann diese hier angelegt werden.
.. code-block:: bash
event_manager
├───event_manager
├───settings
├─base.py
├─dev.py
Inhalt der base.py
----------------------
Diese Inhalte sind die Einstellungen, die in allen Umgebungen benötigt werden. Alle sensiblen Inhalte kommen über die Umgebungsvariablen.
So sieht sie zur Zeit aus:
.. literalinclude:: ../../../src/events/base_settings_1.py
Inhalt der dev.py
----------------------
Diese Datei importiert alle Einstellungen der ``base.py``, überschreibt aber bei Bedarf diejenigen Einstellungen, die im Entwicklungsbetrieb anderes sein müssen.
So sieht sie zur Zeit aus:
.. literalinclude:: ../../../src/events/dev_settings_1.py
Die ``settings_old.py`` können wir nun löschen, wenn wir alles übertragen haben.
.. admonition:: Best Practice: Settings-Dateien versionieren
Das aktuelle Setup ermögtlicht uns, dass auch jeder User eine eigene Settings Datei haben könnte, also zb. ``tom.py`` für den User tom. Trotzdem ist es ratsam, diese privaten, lokalen dev-Settings nicht von der Versionierung auszuschließen. Miskonfigurationen oder bad-pratices können so schneller auffallen.
Bekanntmachen des settings-Folders
------------------------------------
Django weiß von unserer neuen Struktur natürlich nichts. Per default wird nach einer Datei ``settings.py`` im Projektverzeichnis gesucht. Das müssen wir ändern.
Settings-Modul in Env eintragen
.................................
in unsere ``.evn``-Datei legen wir alle Settings, die wir für unseren lokalen Betrieb haben möchte.
So auch das Settings-File. Im folgenden Beispiel sagen wir, dass unsere Settings-Datei
die ``event_manager.settings.dev.py`` sein soll.
Auf einem Live-System würde man hier auf die ``event_manager.settings.prod.py`` verweisen (die wir noch nicht angelegt haben).
.. code-block:: bash
DEBUG=on
SECRET_KEY=2930923943209jalsdf023092384020239840
SETTINGS_MODULE=event_manager.settings.dev
ALLOWED_HOSTS=127.0.0.1 localhost
manage.py
..................................
Wir öffnen die Datei ``event_manager/manage.py`` und fügen folgenden Inhalt ein:
.. code-block:: python
# andere Imports
# environ importieren
import environ
# Settings Modul aus den Environments holen
env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env()
settings_module = env('SETTINGS_MODULE')
def main():
# diese Zeile ändern
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)
# anderer Code
wsgi.py
......................
When the WSGI server loads your application, Django needs to import the settings module — that’s where your entire application is defined.
Django uses the DJANGO_SETTINGS_MODULE environment variable to locate the appropriate settings module. It must contain the dotted path to the settings module. You can use a different value for development and production; it all depends on how you organize your settings.
.. code-block:: python
import os
# environ importieren
import environ
# Settings aus den Umgebungsvariablen laden
env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env()
settings_module = env('SETTINGS_MODULE')
from django.core.wsgi import get_wsgi_application
# diese Zeile ändern
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)
Wenn jetzt der Runserver gestartet wird, sollte alles funktionieren. Wenn wir in der Django-Debugtoolbar auf ``Einstellungen`` gehen, sehen wir, dass die Settings-Datei wie gewünscht unsere ``event_manager.settings.dev`` ist.
Auf der shell kann man sich die Settings so angucken:
.. code-block:: python
>>> from django.conf import settings
>>> settings.__dict__
Wir müssen an mehreren Stellen environ importieren und die Settings laden. Dies
verstößt gegen das ``DRY-Prinzip``. Wir schreiben uns in der Datei
``event_manager/event_manager/utils.py`` eine Helper-Funktion, die dies einmal
erledigt.
.. code-block:: python
import os
from pathlib import Path
import environ
BASE_DIR = Path(__file__).resolve().parent.parent
def getenv():
"""read .env-File from BASE_DIR and create environ instance"""
environ.Env.read_env(os.path.join(BASE_DIR, ".env"))
return environ.Env(
DEBUG=(bool, False),
SETTINGS_MODULE=(str, "event_manager.settings.dev"),
)
Wir brauchen diese Funktion jetzt nur noch zu importieren und nutzen
Zum Beispiel in der ``event_manager/event_manager/settings/base.py``
.. code-block:: python
from ..utils import getenv
# anderer code
env = getenv()