Templates ausbauen
Wir wollen jetzt das bisherige Template ausbauen und ein wenig mehr über die Django-Template-Sprache lernen. Dazu werden wir noch ein Base-Template für das Projekt entwickeln und eine Detailseite für die Kategorie-Informationen erstellen. Wir brauchen dafür natürlich wieder eine URL, eine View und ein Template.
Template-Angaben in den Settings
Wenn wir ein neues Projekt beginnen, setzen wir in der settings.py
Datei
des Projekt zwei wichtige Angaben.
Templates, die das gesamte Projekt betreffen, zb. das Base-Template, müssen für
alle Apps verfügbar sein. Diese Projekt-Templates liegen in einem Ordern, der
in den Settings definiert wird. Für diese Angabe steht der Key DIRS
in den
Template-Settings zur Verfügung. Wir wollen unsere Projekt-Templates also unter
event_manager/templates
speichern. Jede andere Ort wäre aber auch denkbar.
App-spezifische Templates, wie die Übersicht der Kategorien, speichern wir auch
in der jeweiligen App, zb.
event_manager/events/templates/events/categories.html
. Damit
Django in den Apps nach Templates sucht, muss APP_DIRS
in den
settings.py
auf True
gesetzt werden.
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",
],
},
},
]
Mehr dazu unter https://docs.djangoproject.com/en/ref/intro/tutorial03/
Die Template-Hierachie
Das Base-Template
(base.html) enthält alle Elemente und Strukturen, die
jede Page des Projekts haben soll. Zum Beispiel einen Headbereich mit
Navigation, eine Seitenleiste, einen Footer mit Links und so weiter.
Ein Basis-Template ist das grundlegende Template, welches dann auf jeder einzelnen Seite einer Website mit spezifischeren Anweisungen erweitert wird.
Die Sub-Templates definieren ebenfalls diese Blöcke, diesmal allerdings mit
Inhalt gefüllt, und rendern sie in das Base-Template. Das geschieht dadurch, dass das Sub-Template das
Basis-Layout erweitert (extends)
. Durch geschickte Schachtelung, können
tiefe Template-Hierachien geschaffen werden.
ein Base-Template für das Projekt
Unsere projektspezifischen Templates liegen also unter event_manager/templates
. Dort legen wir eine neue Datei
namens base.html
an. In diese Datei kopieren wir folgenden Inhalt:
<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}
<html lang='{{ LANGUAGE_CODE }}'>
<head>
<meta charset="utf-8">
<title></title>
<meta name="author" content="">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>{% block head %}{% endblock %}</h1>
<div>
{% block content %}
{% endblock %}
</div>
</body>
</html>
Es handelt sich um eine einfache HTML-Struktur mit zwei sogenannten Tags
. Diese Tags definieren in diesem Fall Blöcke (block)
, in die später Content gerendert wird. Es gibt aber noch viele andere Tags und wir können natürlich auch selber eigene Tags definieren.
Der LANGUAGE_CODE ist der Code, den wir in der settings.py für die aktuelle
Sprache eingetragen hatten. Er wäre an dieser Stelle aktuell noch nicht
notwendig, aber wir nutzen ihn trotzdem schon mal. Für diesen Zweck hatten wir
{% load i18n %}
ins Template geladen.
In den Block head
soll eine Überschrift gerendert werden, in den Block
content
wird der Inhalt kommen, also zum Beispiel die Liste der Kategorien.
Die Namen head und content sind übrigens von uns frei gewählt, wir könnten sie auch kopf und inhalt nennen.
Diese base.html
wird nicht direkt aufgerufen, sondern wir werden sie quasi vererben.
Das Base-Template erweitern
Um das Base-Template zu nutzen, ändern wir event_manager/events/templates/events/categories.html
wie folgt ab:
{% extends 'base.html' %}
{% block head%}
Übersicht der Kategorien
{% endblock %}
{% block content %}
<ul>
{% for cat in categories %}
<li>
{{cat.name}}
</li>
</a>
{% endfor %}
{% endblock %}
Durch das extends
-Tag erweitern wir unser Template jetzt und definieren den Inhalt, der in die Blöcke von base.html
gerendert werden soll. In dem Fall rendern wir Text in den head
-Block und die Liste mit den Kategorien in den content
-Block.
Wenn wir jetzt mit python manage.py runserver
den Entwicklungsserver
starten und auf die Kategorie-Übersicht navigieren (), sehen wir im Ergebnis
keinen Unterschied zu vorher. Ist ja auch klar, wir hatten am Style an sich
nichts verändert.
Bootstrap HTML
Wir wollen nun das Base-Template nochmal abändern und es ein bisschen besser formatieren. Dazu nutzen wir das Bootstrap-Framework (https://getbootstrap.com/).
Die neue base.html
Datei sieht nun so aus:
<!DOCTYPE html>
{% load i18n %}
{% load static %}
{% get_current_language as LANGUAGE_CODE %}
<html lang='{{ LANGUAGE_CODE }}' class="h-100">
<head>
<meta charset="utf-8">
<title>{% block title %}{% endblock %}</title>
<meta name="author" content="{% block author %}{% endblock %}">
<meta name="description" content="{% block description %}{% endblock %}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="{% static 'css/style.css' %}" rel="stylesheet" />
</head>
<body class="d-flex flex-column h-100">
<header class="p-3 bg-dark text-white">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<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;" />
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0" style="margin-left:120px;">
<li><a href="" class="nav-link px-2
text-white">Events</a></li>
<li><a href="" class="nav-link px-2
text-white">Kategorien</a></li>
</ul>
<form action="" method="get" class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3">
<input name="q" required="required" type="search" class="form-control form-control-dark" placeholder="Search..." aria-label="Search">
</form>
<div class="text-end">
{% if user.is_authenticated %}
<div class="dropdown text-end">
<a href="#" class="d-block link-dark text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<img src="https://github.com/mdo.png" alt="mdo" width="32" height="32" class="rounded-circle">
</a>
<ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="#">Event anlegen</a></li>
{% comment %}
<!-- PASSWORD CHANGE: Freischalten, wenn der Login und Signup implementiert wird -->
<li><a class="dropdown-item" href="{% url 'password_change' %}">Passwort ändern</a></li>
{% endcomment %}
<li><a class="dropdown-item" href="">Passwort ändern</a></li>
<li><hr class="dropdown-divider"></li>
{% comment %}
<!-- LOGOUT: Freischalten, wenn der Login und Signup implementiert wird -->
<li>
<!-- ab django 4.2 ist kein logout per GET mehr erlaubt! -->
<form method="POST"
action="{% url 'logout' %}">
{% csrf_token %}
<button class="dropdown-item">Sign out</button>
</form>
</li>
{% endcomment %}
<li>
<a class="dropdown-item" href="">Sign out</a>
</li>
</ul>
</div>
{% else %}
{% comment %}
<!-- das Freischalten, wenn der Login und Signup implementiert wird -->
<a href="{% url 'login' %}">
<button type="button" class="btn btn-outline-light me-2">Login</button>
</a>
{% endcomment %}
<a href="">
<button type="button" class="btn btn-outline-light me-2">Login</button>
</a>
{% comment %}
<!-- das Freischalten, wenn der Login und Signup implementiert wird -->
<a href="{% url 'user:signup' %}">
{% endcomment %}
<a href="">
<button type="button" class="btn btn-warning">Sign-up</button>
</a>
{% endif %}
</div>
</div>
</div>
</header>
<main class="flex-shrink-0">
<div class="container">
<div class="row">
<div class="col-12" style="margin-top:25px;">
{% block head %}{% endblock %}
</div>
</div>
</div>
<div class="container">
<div class="row">
{% block content %}{% endblock %}
</div>
</div>
</main>
<footer class="footer mt-auto py-3 text-light bg-dark">
<div class="container">
<p class="float-end mb-1">
<a href="#">Back to top</a>
</p>
<h3 class="mb-1">Event Manager</h3>
<p class="mb-0">Neu in Django? <a href="/">Visit the Pingu homepage</a>
oder <a href="https://djangoheroes.spielprinzip.com">Leg los mit Django</a>.</p>
</div>
</footer>
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
</html>
In dem Template finden sich einige Template-Kommentare, die mit dem
comment
-Tag umgesetzt wurden. Insbesonder wurden damit HTML-Elemente und
Verlinkungen auskommentiert, die aktuell noch zu einem Fehler führen würden, da
die entsprechenden Pfade in den urls.py
- Dateien nicht angelegt wurden:
{% comment %}
<!-- das Freischalten, wenn der Login und Signup implementiert wird -->
<a href="{% url 'login' %}">
<button type="button" class="btn btn-outline-light me-2">Login</button>
</a>
{% endcomment %}
In diesem Beispiel sieht man, dass der Login-Button auskommentiert wurde. Den Kommentar werden wir entfernen, wenn wir die Authentifizierung implementieren.
Den Code kann man sich hier bequem runterladen: Code Base HTML:
statische CSS-Datei
Damit dieses Template später funktioniert und richtig angezeigt wird, muss eine
statische CSS-Datei angelegt werden. Dafür werden wir unter
event_manager/static
ein Verzeichnis für statische Dateien anlegen.
Dort erstellen wir noch ein Verzeichnis für CSS-Dateien unter event_manager/static/css/
und legen dort die Datei
style.css
an.
In diese neu erstelle CSS-Datei unter event_manager/static/css/style.css
kopieren wir folgenden Inhalt.
main>.container {
padding: 20px 25px 0;
}
.event_box a {
text-decoration: none;
}
.event_box li {
margin-bottom: 15px;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: ref;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
ul {
padding-left: 0px;
list-style-type: none;
margin-left: 0px;
}
ul li a {
text-decoration: none;
}
.card a {
text-decoration: none;
}
.card h3,
.card p {
color: #333;
}
Der aktuelle Verzeichnisbaum sieht aktuell so aus:
├── event_manager │ ├── db.sqlite3 │ ├── event_manager │ │ ├── settings.py │ │ ├── templates │ │ │ └── base.html │ │ ├── urls.py │ │ └── wsgi.py │ ├── events │ ├── manage.py │ ├── static │ │ ├── css │ │ │ └── style.css │ │ └── images │ │ └── penglogo.png │ └── user │ ├── models.py │ └── views.py ├── README.md ├── requirements-dev.in ├── requirements-dev.txt ├── requirements.in └── requirements.txt
Das neu angelegte static
- Verzeichnis liegt also auf der gleichen Ebene, wie
das manage.py
. Dort befinden sich aktuell die css/style.css
und das
Logo.
Was ist CSS?
Cascading Style Sheets
ist eine Stylesheet-Sprache für elektronische Dokumente und zusammen mit HTML
und JavaScript
eine der Kernsprachen des World Wide Webs. Sie ist ein sogenannter living standard ‚lebendiger Standard‘ und wird vom World Wide Web Consortium beständig weiterentwickelt. (Wikipedia)
statische Logo-Datei
Das Logo kann unter https://github.com/realcaptainsolaris/event_manager_code/raw/main/media/penglogo.png heruntergeladen
und in das statische Verzeichnis unter event_manager/static/images/penglogo.png
kopiert werden.
Auf das HTML bzw. CSS in der Datei einzugehen, würde den Rahmen dieses Buches sprengen. Im Netz finden sich aber unzählige Tutorials, um schnell einen Überblick über CSS zu erhalten.
Im base-Template haben wir noch einen weiteren Block title
eingefügt, um der rerenderten
Seite einen Seitentitel zu geben. Dieser Seitentitel wird zum Beispiel in dem
entsprechenden Brower-Tab angezeigt.
Ändern wir event_manager/events/templates/events/categories.html
noch ab:
{% extends 'base.html' %}
{% block title %}
Übersicht der Kategorien
{% endblock %}
{% block head %}
<h1>Übersicht der Kategorien</h1>
{% endblock %}
{% block content %}
<ul>
{% for cat in categories %}
<li>
{{cat.name}}
</li>
</a>
{% endfor %}
{% endblock %}
Mehr dazu unter https://docs.djangoproject.com/en/ref/topics/templates/
Wenn wir jetzt die Seite unter http://127.0.0.1:8000/events/categories sehen wir zwar ein verändertes
Layout, allerdings fehlt das Logo. Es fällt auch auf, dass die Datei
style.css
nicht geladen werden konnte. Damit diese beiden Resourcen gefunden werden, müssen wir in der
settings.py
noch etwas ändern. Das machen wir aber im nächsten Kapitel
(siehe statische Dateien einbinden).
Intermezzo: die Django-Template-Sprache
Um das Arbeiten mit Templates zu erleichtern, haben wir gesehen, dass uns
Django die Möglichkeit gibt, direkt im Template über Listen zu iterieren oder Variablen
auszugeben. Und dazu haben wir die sogenannte Django-Template-Sprache genutzt.
Solche Template-Engines gibt es in den meisten Web-Frameworks in ähnlicher Art
und Weise, zum Beispiel das bekannte Twig
aus dem PHP-Framework Symphony
1.
Wozu noch eine neue Sprache?
Wenn wir Daten an den User ausgeben wollen, gehört diese Aufgabe zur sogenannten Präsentationsebene. Die Datenverarbeitung in den Views und Models ist eher auf der Applikationslogik zu finden. Generell gilt: die Formatierung und optische Aufbereitung von Daten zum Zweck der Präsentation darf nicht in der Appikationsebene vorgenommen werden. Wir wollen Logik und Ausgabe strikt getrennt halten.
Variablen
Wenn die Template-Engine auf eine Variable trifft, die als Key im Kontext-Dictionary übergeben wurde, wertet sie
diese Variable aus und rendert sie in das Template. Variablen sehen so aus: {{ Variable }}
.
Variablennamen bestehen aus einer beliebigen Kombination von alphanumerischen Zeichen und dem Unterstrich („_“), dürfen jedoch nicht mit einem Unterstrich beginnen. Falls für eine Variable kein Wert verfügbar ist, resultiert das in keinem Fehler.
Oben im Code sehen wir die Variable {{cat.name}}
. Dort wird das name-Attribut eines Kategorie-Objekts gerendert.
Wir haben in den Variablen via der Dot-Notation also auch ganz wie gewohnt Zugriff auf die Attribute
eines Objekts, wie wir das von Python gewohnt sind.
Filter
Der Inhalt der Variablen kann im Template an Filter übergeben werden, die vom Prinzip her ähnlich aufgebaut sind, wie der Pipe-Operator auf der Unix - Shell.
Ein Filter kann über diesen Operator an die Variable angehängt und deren Ausgabe verändern, zum Beispiel den Beschreiungstext in Kleinbuchstaben ausgeben:
{{ company.description|lower }}
|
enspricht dem Capitalize aus Python |
|
zur schönen Formatierung von Datumsangaben |
|
Der Defaultwert einer leeren Variable |
|
konvertiert Zeichen in HTML-Entsprechungen |
|
Konvertiert Newlines in HTML-Breaks |
|
pluralisiert Wörter |
|
löscht HTML-Tags |
Filter lassen sich auch chainen, dh. verketten. Der fertige Filter reicht den Output an den nächsten Filter in der Reihe weiter:
{{ company.description|striptags|lower|capfirst }}
So wird die Beschreibung erst von HTML-Tags bereinigt, dann in Kleinbuchstaben unmgewandelt um schließlich via capfirst alles wieder zu Kapitalisieren. Erst dann wird die Variable in den HTML-Code gerendert.
Der Vollständigkeitshalber sei noch erwähnt, dass es nicht sonderlich schwierig ist, Filter selber zu erstellen. Filter sind nichts anderes als Python-Funktionen, die man registrieren und laden muss, um sie im Template zu nutzen. Mehr dazu findet sich in der Doku.
Weitere Built-in Filter finden sich hier: https://docs.djangoproject.com/en/stable/ref/templates/builtins/#filter
Die Detailseite einer Kategorie
Um von der Kategorie-Übersicht auf die Detailseite einer Kategorie weiterzuleiten, müssen wir die Liste verlinken. Die URL zur Detailseite zu einer Kategorie ist allerdings noch nicht festgelegt, das müssten wir machen, bevor wir die Verlinkung einbauen können.
Dazu ändern wir die urlpatterns
in den event_manager/events/urls.py
ab:
urlpatterns = [
path("hello_world", views.hello_world, name="hello_world"),
path("categories", views.categories, name="categories"),
# diese Zeile hinzufügen:
path("category/<int:pk>", views.category_detail, name="category_detail"),
]
Das heisst, wenn wir http://127.0.0.1:8000/events/category/3
via Browser aufrufen, sollte uns die Detailansicht einer Kategorie ausgegeben werden.
Die View für die Detailseite der Kategorie
Die View category_detail
existiert in den views.py
noch nicht, deshalb legen wir sie an. Lade das entsprechende Objekt aus der Datenbank, und werfen eine 404 Not Found Exception, falls das Objekt nicht gefunden wird.
Die Http404 Exception muss importiert werden:
from django.shortcuts import render
from django.http import Http404
from .models import Event, Category
def category_detail(request, pk: int):
"""
die Detailseite einer Kategorie
http://127.0.0.1:8000/events/category/3
"""
try:
category = Category.objects.get(pk=pk)
except Category.DoesNotExist:
raise Http404("Diese Kategorie existiert nicht")
return render(request, 'events/category_detail.html', {
'category': category
})
404 Fehler
Wenn eine URL nicht angezeigt werden kann, weil zum Beispiel das gesuchte Objekt nicht in der Datenbank vorhanden ist, lösen wir einen Http404-Fehler
aus. Dem User wird daraufhin eine 404-Fehlerseite angezeigt (Not found), die wir später noch als Template anlegen werden.
Falls wir in den event_manager/settings.py
DEBUG=True
gesetzt haben, bekommen wir eine ausführliche Fehlerseite angezeigt, die uns detailliert aufzeigt, wo das Problem liegt. Im Live-Modus sollte zwingend DEBUG=False
sein. Dann könnten wir unsere eigene 404-Fehlerseite
anzeigen.
Wir rendern das Kategorieobjekt category
in das Template event_manager/events/templates/events/category_detail.html
.
Das Template für die Detailseite der Kategorie
Legen wir eine neue Datei event_manager/events/templates/events/category_detail.html
im Template-Ordner der App an und kopieren folgenden Inhalt hinein:
{% extends 'base.html' %}
{% load i18n %}
{% block title %}
Kategorie {{category}}
{%endblock%}
{% block head %}
<h1>Kategorie {{category}}</h1>
{%endblock%}
{% block content %}
<p><a href="{% url 'events:categories' %}">zurück zur Kategorie Übersicht</a></p>
<p>{{category.description}}</p>
<p>created at: {{category.created_at}}</p>
{%endblock%}
Wir definieren hier unter anderem einen Link, der uns zurück zur Übersichtsseite führt. Wie solche Verlinkungen in Django funktionieren, sehen wir gleich weiter unten genauer.
Wenn wir jetzt eine Detailseite aufrufen, sollte das klappen. Dazu starten wir
den Runsever mit python manage.py runserver
und navigieren auf die URL
http://127.0.0.1:8000/events/category/3
. Falls hier ein Not-Found Error
kommt, muss womöglich eine andere ID gewählt werden (dazu in der
Admin-Oberfläche nachschauen, welche Kategorien vorhanden sind).
Prüfung ob Vorhanden und der 404-Fehler
In der Funktion category_detail
hatten wir im try-except-Block geprüft,
ob das Selektieren des Objekts über die übergebenen ID keinen Fehler schmeisst,
und hatten im Erfolgsfall das Objekt in category gespeichert. Im Fehlerfall
hatten wir mit raise
eine Http404
-Exception ausgelöst.
Dieser Fall, also das Prüfen des Vorhandenseins eines Objektes, ist ein so
häufiger Use-Case, dass es dafür einen Shortcut gibt: die Funktion
get_object_or_404
. Die macht das, was wir vorhin umständlich gemacht
hatten, in einer Zeile.
Wir ändern die View event_manager/events/views.py
und ersetzen die Funktion
category_detail
mit dieser hier. Am Seitenanfang importieren wir noch
get_object_or_404
aus den Shortcuts.
from django.shortcuts import render, get_object_or_404
from django.http import Http404
def category_detail(request, pk: int):
"""
http://127.0.0.1:8000/events/category/3
"""
category = get_object_or_404(Category, pk=pk)
return render(request, 'events/category_detail.html', {
'category': category
})
get_object_or_404
Für die immerwiederkehrende Aufgabe, ein Objekt aus der Datenbank zu holen
und per try-except zu prüfen, ob es in der Datenbank vorhanden ist, nutzen
wir ab jetzt den Shortcut get_object_or_404
, den wir aus den
django.shortcuts
importieren. Wenn sich das Objekt nicht in der
Datenbank befinden, wird ein 404-Fehler ausgelöst und dem User wird eine
404-Fehlerseite anzeigt.
Wir versuchen jetzt, die URL so aufzurufen: http://127.0.0.1:8000/events/category/3
Die Detailseite verlinken
Zuletzt wollen wir noch die Verlinkung auf die Kategorie bauen.
Ändern wir dazu event_manager/events/templates/events/categories.html
noch ab.
{% extends 'base.html' %}
{% block title %}
Übersicht der Kategorien
{% endblock %}
{% block head %}
<h1>Übersicht der Kategorien</h1>
{% endblock %}
{% block content %}
<ul>
{% for category in categories %}
<li>
<a href="{% url 'events:category_detail' category.pk %}">{{category.name}}</a>
</li>
</a>
{% endfor %}
{% endblock %}
Pro Schleifendurchlauf bauen wir ein Listen-Elemente li, und setzen dort einen Hyperlink zu der jeweiligen Kategorie. Der Hyperlink wird mit einem URL-Tag angegeben, der in der Form APP-Name:PFAD-Name angeben wird, sowie dahinter die jeweiligen Parameter.
Mehr dazu in der Doku: https://docs.djangoproject.com/en/stable/templates/builtins/#url
Die Verlinkungen sollten jetzt blau hinterlegt sein:
Verlinkungen mit dem URL-Tag
Einen Template-Tag, den wir noch nicht besprochen haben, ist der url
-Tag.
Dieser Tag ist dafür da, URLs zu generieren. Zum Erstellen der URL sind zwei
Dinge wichtig: der app_name
in der urls.py und der Name des Pfades, so wie
er in der path
-Funktion der urlpatterns festgelegt wurde.
Der URL-Tag hat immer folgendes Schema:
{% url „<APP_NAME:PATH_NAME>“ <PATH_ARGUMENTS (optional)> %}
APP_NAME:
das entspricht der Variableapp_name
aus denurls.py
der App, also hierevents
(siehe Das Anlegen der ersten App).PATH_NAME:
das enstpricht demname
-Argument der path-Funktion aus den App-Urls.PATH_ARGUMENTS: falls unsere Route einen oder mehrere Parameter definiert hat, in unserem Fall also einen Primary Key, werden diese als Leerzeichen-separierte Liste angegeben.
path("category/<int:pk>", views.category_detail, name="category_detail")
Mehr zum Thema url-Template Tag
in der Django-Doku:
https://docs.djangoproject.com/en/stable/templates/builtins/#url
URL-Tag vs. hardkodierte URL
Wir erstellen die Verlinkungen über den URL-Tag
, und das ist auch der
richtige Weg, dies zu tun. Wir könnten die Verlinkung natürlich auch direkt
hardkodiert erstellen, zb so:
<a href="/events/category/{{cat.pk }}">{{cat}}</a>
Das würde gehen, gilt aber als schlechter Stil. Falls sich nämlich der
URL-Pfad der Route ändern würde, zum Beispiel von events/category
zu
events/genre
, müsste man den Link an allen Stellen, wo man ihn so
hardkodiert definiert hat, manuell ändern. Und diese Änderungen kommen häufiger vor, als man meinen würde.
Wenn wir jetzt den runserver
starten, sollten wir unter http://127.0.0.1:8000/events/categories
die Kategorien in einer Liste sehen.