Context Processors

Manchmal ist es notwendig, Werte aus der Datenbank auf allen Seiten der Website auszugeben, zum Beispiel der Name der Website.

Konkret heisst das für uns, dass jede View zusätzlich noch ein weiteres Objekt im Kontext an das Template überreichen müsste. Das klingt ziemlich aufwändig.

Natürlich könnte man den Wert auch hardkodiert in den Settings hinterlegen, nur wäre er dann nicht von Moderatoren oder anderen Staff-Membern editierbar. Bei einer Änderung an den Settings müsste das System neu deployed werden. Besser wäre es, wenn wir solche Aufgaben über die Administrationsoberfläche lösen könnten.

Mit den Methoden, die wir bisher kennengelernt haben, ist das aufwändig.

Was ist ein Context-Prozessor?

Mit Context-Prozessoren wird zusätzlicher Kontext an die Template-Engine übergeben, ohne ihn explizit in jeder View mit angeben zu müssen, was sehr müheslig wäre. Django besitzt schon ein paar built-in Context-Prozessoren, zum Beispiel das Übergeben der Success-Messages an eine View.

Diese Kontext-Prozessoren sind in der settings.py in der TEMPLATES-Konstante angelegt. Der Message-Kontextprozessor sieht zum Beispiel so aus:

django.contrib.messages.context_processors.messages

Eigene Context-Prozessoren entwickeln

Wir können natürlich auch eigene Kontext-Prozessoren entwickeln und in den Settings hinterlegen. Wir wollen jetzt den Namen der Website über die Administrationsoberfläche verfügbar machen und über einen Kontext-Prozessor auf jeder View ausgeben.

Es bietet sich also an, für System-Settings ein eigenes Modell anzulegen, das später über die Administrationsoberfläche editiert werden kann.

Django Site Framework

Django wird mit dem sogenannten Sites-Framework ausgeliefert, welches uns ermöglicht, für unser Projekt Namen und Web-Adresse in der Datenbank zu spezifieren. Wir können auch mehrere solcher Sites anlegen und könnten dann in Abhängigkeit von der genutzten Domain unterschiedlichen Content ausgeben.

Dies ist besonders nützlich, wenn wir mehrere Websites mit einer einzigen Codebase (zum Beispiel in einem Content-Management-System) oder mehrere Websites mit einer gemeinsamen Datenbank verwalten.

Das wollen wir hier in diesem Buch allerdings nicht weiter vertiefen. Uns reicht die Möglichkeit, einen festen Namen und eine Web-Adresse für unsere SITE in der Admin zu hinterlegen und editierbar zu machen.

Mehr zu dem Thema findet sich in der Django-Doku: https://docs.djangoproject.com/en/stable/ref/contrib/sites/

Um das Sites-Framework zu installieren, müssen wir es nur in den INSTALLED_APPS in den event_manager/event_manager/settings/base.py eintragen.

INSTALLED_APPS = [
  ...
  "django.contrib.sites",
  ...
]

und ebenfalls in der event_manager/event_manager/settings/base.py die Middleware hinzufügen.

MIDDLEWARE = [
    ...
     "django.contrib.sites.middleware.CurrentSiteMiddleware",
    ...
]

Nun führen wir die Migration aus:

(eventenv) python manage.py migrate


Running migrations:
Applying sites.0001_initial... OK
Applying sites.0002_alter_domain_unique... OK

und schon ist das Sites-Framework installiert.

In den Settings unter event_manager/event_manager/settings/base.py können wir jetzt noch die SITE_ID festlegen. Diese setzen wir auf 1 (weil wir auch nur eine Site festlegen).

SITE_ID = 1

Die Admin Oberfläche

Wenn wir jetzt die Administrationsoberfläche öffnen, sehen wir auf der linken Seite einen neuen Eintrag namens Webseiten.

../_images/django_sites_1.png

Wir können jetzt den Namen der Webseite (Anzeigename) und die Adresse ändern und speichern. Momentan hat das noch keinen Einfluss auf unsere Website, da wir die Daten bisher nicht ausgeben.

../_images/django_sites_2.png

Unser Context Processor

Wir wollen den Anzeigename unserer Site jetzt in der Top-Navigation unserer Webseite ausgeben. Wenn wir den bisherigen Weg gehen wollten, müssten wir jetzt jeder View diesen Wert hinzufügen. Sehr umständlich.

Wir gehen den Weg über den Context Processor.

Unter event_manager/event_manager legen wir die Datei context_processors.py an. Diese Datei enthält eine Funktion, die ein Kontext-Dictionary zurückgibt.

from django.contrib.sites.models import Site

def get_site_infos(request):
  current_site_name = Site.objects.get_current().name
  current_site_domain = Site.objects.get_current().domain

  return {'site_name':current_site_name, 'site_domain': current_site_domain}

get_site_infosa ist ein benutzerdefinierter Kontextprozessor, den wir gerade erstellt haben. Wir können diesen jetzt den context_processors in den TEMPLATES"-Settings unter event_manager/eventmanager/settings/base.py hinzufügen.

Der Key site_name aus dem zurückgegebenem Dictionary ist dann als Template-Variable in allen Templates zugänglich.

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "event_manager" / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
                "event_manager.context_processors.get_site_infos",
            ],
        },
    },
]

Das Template anpassen

Wir wollen den Website-Namen jetzt überall auf unserer Website ausgeben. Deshalb öffnen wir das base-Template unter event_manager/event_manager/templates/base.html und geben die Kontextvariable site_name aus.

Etwa in Zeile 23 der base.html fügen wir nach dem img-Tag des Logos noch den site_name ein:

...

<a href="/" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
    <img src="{% static 'images/penglogo.png' %}" style="width:50px; margin-right:10px;" />

    {{site_name}}<!-- DAS einfügen! -->
</a>

<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0" style="margin-left:0px;">
...

Wenn wir die Website unter http://127.0.0.1 neu laden, sollte der Name der Webseite oben links neben dem Pinguin-Logo erscheinen.

Mehr zum Thema Context Processor in der Django Doku: * https://docs.djangoproject.com/en/stable/ref/templates/api/#writing-your-own-context-processors

Optional: Das Projekt in der Version v0.4 kopieren

Wie immer kann man sich den Zwischenstand klonen, falls beim Mitschreiben was schiefgegangen ist.

Version v0.5 via github clonen

git clone -b v0.5 git@github.com:realcaptainsolaris/event_project.git

und dann nicht vergessen, ein virtuelles Environment anzulegen und zu migrieren und danach noch ein paar Testdaten zu generieren:

python -m venv .envs/eventenv
pip install pip-tools
pip-sync requirements.txt requirements-dev.txt
python manage.py migrate
python manage.py createsuperuser
python manage.py create_user -n 10
python manage.py create_events --events 20 --categories 5
python manage.py runserver

Version v0.5 als zip-Datei runterladen

https://github.com/realcaptainsolaris/event_project/archive/refs/tags/v0.5.zip

Version v0.5 als tar-Datei runterladen

https://github.com/realcaptainsolaris/event_project/archive/refs/tags/v0.5.tar.gz