.. _pages_app: .. index:: single: Pages single: Homepage single: Impressum single: Startseite single: TemplateView single: get_context_data Eine Pages App erstellen ************************* Bevor wir mit der Events-App weitermachen, wollen wir noch eine ``pages``- App erstellen, die mehrheitlich statische Seiten wie zum Beispiel die ``Homepage`` oder das Impressum bereitstellt. Fangen wir mal mit der Homepage an. App installieren ================== Um eine App innerhalb eines Projekts anzulegen, nutzen wir wie gewohnt das Sub-Kommando ``startapp`` gefolgt von dem Namen der gewünschten App. .. code-block:: shell (eventenv) python manage.py startapp pages und registrieren sie in den ``event_manager/event_manager/settings.py`` .. code-block:: python INSTALLED_APPS = [ "user", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "crispy_bootstrap5", "crispy_forms", "events", "pages" ] Die URLS anlegen ======================= Projekt URLs erweitern ------------------------ Dazu öffnen wir erstmal die Projekt-URLs unter ``event_manager/event_manager/urls.py`` und fügen den Path zu den ``pages.urls`` ein: .. code-block:: python from django.contrib import admin from django.urls import include, path urlpatterns = [ path("admin/", admin.site.urls), path("events/", include("events.urls", namespace="events")), path("", include("pages.urls", namespace="pages")), # <= das hier einfügen! ] Dadurch, dass das erste Argument für die ``path``-Funktion ein leerer String ist, können wir erreichen, dass unsere Index-Page bzw. das Impressum unter der Hauptdomain angezeigt wird. Die URL für die Index-Seite wäre dann zum Beispiel ``www.example.com`` und für das Impressum ``www.example.com/imprint``. Alle anderen Apps müssen wie gewohnt mit ihrem Pfad aufgerufen werden. App URLs anlegen ------------------------ Jetzt müssen noch die URLs auf App-Ebene festgelegt werden. Dazu legen wir die Datei ``event_manager/pages/urls.py`` an und modifzieren den Inhalt wie folgt: .. code-block:: python from django.urls import path from . import views app_name = "pages" urlpatterns = [ path('', views.HomePageView.as_view(), name='home'), ] View für die Homepage einrichten =================================== Die View, auf die wir in den urlpatterns referenzieren, muss jetzt noch unter ``event_manager/pages/views.py`` angelegt werden. .. code-block:: python from django.views.generic.base import TemplateView class HomePageView(TemplateView): """Das ist eine generische, klassenbasierte View.""" template_name = "pages/index.html" Template für die Homepage =================================== Wir legen das Template für die Homepage unter ``event_manager/pages/templates/pages/index.html`` an und fügen folgenden Inhalt ein: .. code-block:: html+django {% extends "base.html" %} {% block title %}Homepage{% endblock %} {% block head %}

Event Manager Startseite

{% endblock %} Wenn wir das die URL ``127.0.0.1:8000`` aufrufen, sollte uns jetzt die Homepage angezeigt werden. .. image:: /images/homepage_1.png Auf die selbe Art könnte man nun weitere Pages wie zum Beispiel ein Impressum oder eine About-Seite anlegen. Das überlasse ich mal dem Leser als kleine Übungsaufgabe. Den Kontext für die Homepage verändern ======================================== Wir wollen auf der Homepage nun die top 5 Kategorien auflisten, die die meisten Events haben. Dazu müssen wir einen Filter erstellen und die Methode ``get_context_data`` überschreiben. .. code-block:: python from django.views.generic.base import TemplateView from django.db.models import Count from events.models import Category class HomePageView(TemplateView): template_name = "pages/index.html" def get_context_data(self, **kwargs) -> dict: context = super().get_context_data(**kwargs) categories = Category.objects.annotate( number_events=Count("events") ).order_by("-number_events")[:5] context.update({"categories": categories}) return context Dann passen wir nochmal das Homepage-template unter ``event_manager/pages/templates/pages/index.html`` an und fügen folgenden Inhalt ein: .. code-block:: html+django {% extends "base.html" %} {% block title %}Homepage{% endblock %} {% block head %}

Event Manager Startseite

{% endblock %}

Kategorien

.. image:: /images/homepage_2.png Kleine Übung ======================================== Wer will, kann jetzt versuchen, zusätzlich noch 5 zufällige Events auf der Startseite darzustellen. Dazu lässt sich die gut die ``sample`` Funktion des ``random``-Moduls nutzen. Die Lösung könnte dann aussehen: .. code-block:: python from random import sample from django.views.generic.base import TemplateView from django.db.models import Count from events.models import Category, Event def get_sample_events(n: int): """Erzeuge eine zufällige Liste mit n Event-Objekten.""" return sample(list(Event.objects.all()), n) class HomePageView(TemplateView): template_name = "pages/index.html" def get_context_data(self, **kwargs) -> dict: context = super().get_context_data(**kwargs) categories = Category.objects.annotate( number_events=Count("events") ).order_by("-number_events")[:5] events = get_sample_events(n=5) context.update({"categories": categories, "events": events}) return context Im Template würde man die erzeugten Events dann so ausgeben: .. code-block:: html+django

Events

.. image:: /images/homepage_3.png Die Abfrage ist allerdings aus Performance-Sicht nicht ideal. Wir erstellen erst eine Liste aller Objekte und iterieren dann über die Objekte, während wir uns im Template auf den Autor sowie die Kategorie beziehen. Wir müssen also die Tabellen, die zu Event in Beziehung stehen, noch vorladen, diesmal nutzen wir dazu ``select_related``: .. code-block:: python def get_sample_events(n: int): """Erzeuge eine zufällige Liste mit n Event-Objekten.""" return sample(list(Event.objects.select_related("author","category").all()), n) Die Anzahl der Datenbank-Anfragen hat sich nun drastisch reduziert: .. image:: /images/homepage_4.png