Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
1 Einleitung
2 Die Programmiersprache Python
Teil I Einstieg in Python
3 Erste Schritte im interaktiven Modus
4 Der Weg zum ersten Programm
5 Kontrollstrukturen
6 Dateien
7 Das Laufzeitmodell
8 Funktionen, Methoden und Attribute
9 Informationsquellen zu Python
Teil II Datentypen
10 Das Nichts – NoneType
11 Operatoren
12 Numerische Datentypen
13 Sequenzielle Datentypen
14 Zuordnungen
15 Mengen
16 Collections
17 Datum und Zeit
18 Aufzählungstypen – Enum
Teil III Fortgeschrittene Programmiertechniken
19 Funktionen
20 Modularisierung
21 Objektorientierung
22 Ausnahmebehandlung
23 Iteratoren
24 Kontextobjekte
25 Manipulation von Funktionen und Methoden
Teil IV Die Standardbibliothek
26 Mathematik
27 Kryptografie
28 Reguläre Ausdrücke
29 Schnittstelle zu Betriebssystem und Laufzeitumgebung
30 Kommandozeilenparameter
31 Dateisystem
32 Parallele Programmierung
33 Datenspeicherung
34 Netzwerkkommunikation
35 Debugging und Qualitätssicherung
36 Dokumentation
Teil V Weiterführende Themen
37 Anbindung an andere Programmiersprachen
38 Distribution von Python-Projekten
39 Grafische Benutzeroberflächen
40 Python als serverseitige Programmiersprache im WWW – ein Einstieg in Django
41 Wissenschaftliches Rechnen
42 Insiderwissen
43 Von Python 2 nach Python 3
A Anhang
Stichwortverzeichnis

Download:
- Beispielprogramme, ca. 464 KB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Python 3 von Johannes Ernesti, Peter Kaiser
Das umfassende Handbuch
Buch: Python 3

Python 3
Pfeil 40 Python als serverseitige Programmiersprache im WWW – ein Einstieg in Django
Pfeil 40.1 Konzepte und Besonderheiten von Django
Pfeil 40.2 Installation von Django
Pfeil 40.2.1 Installation mit Anaconda
Pfeil 40.2.2 Für Leser, die Anaconda nicht verwenden
Pfeil 40.3 Erstellen eines neuen Django-Projekts
Pfeil 40.3.1 Der Entwicklungswebserver
Pfeil 40.3.2 Konfiguration des Projekts
Pfeil 40.4 Erstellung einer Applikation
Pfeil 40.4.1 Die Applikation in das Projekt einbinden
Pfeil 40.4.2 Ein Model definieren
Pfeil 40.4.3 Beziehungen zwischen Modellen
Pfeil 40.4.4 Übertragung des Modells in die Datenbank
Pfeil 40.4.5 Das Model-API
Pfeil 40.4.6 Unser Projekt bekommt ein Gesicht
Pfeil 40.4.7 Djangos Template-System
Pfeil 40.4.8 Verarbeitung von Formulardaten
Pfeil 40.4.9 Djangos Administrationsoberfläche
 
Zum Seitenanfang

40.4    Erstellung einer Applikation Zur vorigen ÜberschriftZur nächsten Überschrift

Ein Projekt ist nur der Rahmen für eine Webseite. Die eigentliche Funktionalität wird durch die Applikationen implementiert, die in das Projekt eingebunden werden. Genau wie für Projekte bietet Django auch für Applikationen ein Werkzeug an, um das Grundgerüst einer Applikation zu erzeugen.

Um unsere News-Applikation zu anzulegen, wechseln wir mit einer Konsole in das Projektverzeichnis, in dem die Datei manage.py liegt, und führen den folgenden Befehl aus (das python am Anfang kann unter Windows entfallen).

$ python manage.py startapp news

Das Programm erzeugt in unserem Projektverzeichnis einen neuen Ordner namens news, der ein weiteres Verzeichnis und mehrere Dateien enthält (siehe Abbildung 40.3). Dabei ist news der Name der neuen Applikation.

Ordner einer neuen Django-Applikation

Abbildung 40.3    Ordner einer neuen Django-Applikation

Für uns sind zunächst die beiden Dateien models.py und views.py interessant, die dazu dienen, das Django zugrunde liegende Model-View-Konzept umzusetzen.

Das sogenannte Model definiert die Struktur unserer Daten in der Datenbank. Außerdem bieten Models eine komfortable Schnittstelle für den Zugriff auf die gespeicherten Daten. Wie die konkrete Kommunikation mit der Datenbank abläuft, braucht den Programmierer nicht zu interessieren. Er kann beim Umgang mit den Daten auf die Schnittstellen des Models zurückgreifen, ohne sich um technische Details wie SQL-Statements kümmern zu müssen.[ 210 ](Natürlich ist es möglich, eigene SQL-Befehle an die Datenbank zu senden, falls die Modellschnittstelle von Django für einen speziellen Fall nicht ausreichen sollte. In der Praxis werden Sie allerdings nur in Ausnahmefällen davon Gebrauch machen müssen. ) Die Datenbank selbst bleibt vor ihm »verborgen«, weil er sie nur indirekt durch das Model »sieht«. Insbesondere werden anhand der Model-Definition automatisch alle benötigten Tabellen in der Datenbank angelegt.

Das Model-View-Konzept

Abbildung 40.4    Das Model-View-Konzept

Die sogenannte View (dt. Ansicht) kümmert sich um die Aufbereitung der Daten für den Benutzer. Sie kann dabei auf die Models zurückgreifen und deren Daten auslesen und verändern. Wie dabei die Benutzerschnittstelle konkret aussieht, ist der View egal. Die Aufgabe der Views ist nur, die vom Benutzer abgefragten Daten zu ermitteln, diese zu verarbeiten und dann an ein sogenanntes Template zu übergeben, das die eigentliche Anzeige übernimmt. Mit Templates werden wir uns später beschäftigen.

 
Zum Seitenanfang

40.4.1    Die Applikation in das Projekt einbinden Zur vorigen ÜberschriftZur nächsten Überschrift

Wir müssen Django noch mitteilen, dass unsere neu erstellte Applikation in das Projekt news_seite eingebunden werden soll. Dazu fügen wir die Applikation news zum Tupel INSTALLED_APPS in der settings.py hinzu.

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'news',
]

Die Liste INSTALLED_APPS enthält dabei die Importnamen aller Applikationen, die das Projekt verwendet. Sie werden sich jetzt wundern, warum unser eigentlich leeres Projekt schon einige Applikationen enthält. Django bindet diese Applikationen standardmäßig ein, weil sie in eigentlich jedem Projekt gebraucht werden.

 
Zum Seitenanfang

40.4.2    Ein Model definieren Zur vorigen ÜberschriftZur nächsten Überschrift

Als Nächstes definieren wir ein Datenmodell für unsere Applikation news. Das Datenmodell enthält dabei für jede Art von Datensatz der Applikation eine Python-Klasse. Diese Klassen müssen von der Basisklasse models.Model im Paket django.db abgeleitet werden und legen die Eigenschaften der Datensätze und ihre Verknüpfungen untereinander fest.

Unser Beispielmodell für die News-Applikation definiert eine Model-Klasse für die Meldungen und eine für die Besucherkommentare. Wir schreiben die Definition in die Datei models.py, die dann Folgendes enthält:

from django.db import models

class Meldung(models.Model):
titel = models.CharField(max_length=100)
zeitstempel = models.DateTimeField()
text = models.TextField('Meldungstext')

class Kommentar(models.Model):
meldung = models.ForeignKey(Meldung)
autor = models.CharField(max_length=70)
text = models.TextField('Kommentartext')

Die Attribute der Datensätze werden über Klassenmember festgelegt, wobei jedes Attribut eine Instanz eines speziellen Feldtyps[ 211 ](Django stellt für viele Anwendungsfälle Feldtypen zur Verfügung. Wir beschränken uns hier nur auf solche Feldtypen, die für unser Beispielprojekt relevant sind. ) von Django sein muss. Über die Parameter der Feldtyp-Konstruktoren werden dabei die Eigenschaften der Attribute angegeben.

Die Klasse Meldung besitzt ein CharField namens titel, das eine maximale Länge von 100 Zeichen hat. Der Feldtyp CharField dient zum Speichern von Texten begrenzter Länge. Das Attribut zeitstempel soll den Veröffentlichungszeitpunkt jeder Meldung angeben und benutzt den für Zeitangaben gedachten Feldtyp DateTimeField. Im letzten Attribut namens text wird der eigentliche Meldungstext gespeichert. Der verwendete Feldtyp TextField kann beliebig lange Texte speichern.

Die beiden Attribute autor und text der Klasse Kommentar speichern den Namen desjenigen Besuchers, der den Kommentar geschrieben hat, und den Kommentartext selbst.

Interessant ist das Attribut meldung, mit dem eine Beziehung zwischen den Meldungen und Kommentaren hergestellt wird.

 
Zum Seitenanfang

40.4.3    Beziehungen zwischen Modellen Zur vorigen ÜberschriftZur nächsten Überschrift

Zu einer Meldung kann es mehrere Kommentare geben, und umgekehrt bezieht sich jeder Kommentar auf eine Meldung. Mit dem Feldtyp ForeignKey (dt. Fremdschlüssel) wird eine One-To-Many Relation (dt. Eins-zu-viele-Relation) festgelegt, die besagt, dass es zu einem Kommentar genau eine Meldung gibt.

One-To-Many Relation bei Meldung und Kommentar

Abbildung 40.5    One-To-Many Relation bei Meldung und Kommentar

Neben den One-To-Many Relations unterstützt Django auch Many-To-Many Relations (dt. Viele-zu-vielen-Relationen) und One-To-One Relations (dt. Eins-zu-eins-Relationen), die wir aber nicht thematisieren werden.

 
Zum Seitenanfang

40.4.4    Übertragung des Modells in die Datenbank Zur vorigen ÜberschriftZur nächsten Überschrift

Nachdem wir nun unsere Applikation erzeugt, sie in das Projekt eingefügt und ihr Datenmodell definiert haben, können wir die dazugehörige Datenbank erstellen. Dazu werfen wir einen Blick auf Djangos Mechanismus, der das Datenbanklayout mit dem Datenmodell synchronisiert.

Während der Entwicklung mit Django legen Sie das Datenbanklayout indirekt über die Modelle fest, die Sie in der Datei models.py definieren. Deshalb müssen Änderungen aus models.py in die Datenbank übertragen werden, damit das Datenbanklayout zu Ihren Modellen passt. Insbesondere entstehen Inkonsistenzen, wenn Sie ein Modell ändern, ohne dass die Datenbank entsprechend angepasst wird.

In unserem Beispiel haben wir in der Datei models.py die Modelle Meldung und Kommentar angelegt. Die Datenbank unseres Projekts ist allerdings noch leer. Diese Inkonsistenz werden wir nun beseitigen.

Djangos Migrations

Um Änderungen am Datenmodell komfortabel flexibel auf die Datenbank zu übertragen, bietet Django Migrations an. Eine Migration ist ein Python-Programm, das Änderungen des Datenmodells beschreibt. Django bietet Befehle an, um diese Migrations automatisch zu erzeugen, sodass Sie als Programmierer weitgehend von den technischen Details verschont bleiben.[ 212 ](Es ist aber ausdrücklich vorgesehen, dass der Programmierer manuell Migrations anpasst, wenn Djangos Automatismen einmal nicht ausreichen sollten. ) Mit dem folgenden Kommando erzeugen wir die Migration für das initiale Datenbanklayout für unsere Applikation news, wobei der Befehl python am Anfang unter Windows entfällt.

$ python manage.py makemigrations news
Migrations for 'news':
news/migrations/0001_initial.py:
- Create model Kommentar
- Create model Meldung
- Add field meldung to kommentar

Nun existiert eine Datei 0001_initial.py im Verzeichnis news/migrations, die das Datenbanklayout unserer Beispielapplikation beschreibt. Um dieses Layout in die Datenbank zu schreiben, nutzen wir den Befehl migrate, dessen Ausgabe hier nicht abgedruckt wird.

$ python manage.py migrate 

Die längliche Ausgabe des Befehls teilt uns mit, welche Migrations zu welchen Applikationen ausgeführt worden sind. Die Details sollen uns hier nicht weiter interessieren. Wichtig ist nur, dass wir nun eine Datenbank erzeugt haben, die zu unserem Datenmodell passt.

[»]  Hinweis

Das Konzept der Migrations ist sehr mächtig und ermöglicht komfortabel, das Datenbanklayout nachträglich zu verändern oder auch Änderungen rückgängig zu machen.

Der Arbeitsablauf gestaltet sich dabei folgendermaßen:

  1. Sie verändern Ihr Datenmodell in der Datei models.py.
  2. Sie erzeugen eine Migration mit dem Kommando
  3. $ python manage.py makemigrations
  4. Sie übertragen die Änderungen in die Datenbank mit
    $ python manage.py migrate

Mehr dazu erfahren Sie in der Django-Dokumentation unter https://docs.djangoproject.com/en/1.10/topics/migrations/.

Unser Beispielprojekt ist damit initialisiert, sodass wir uns der Verwaltung von Meldungen und Kommentaren zuwenden können.

 
Zum Seitenanfang

40.4.5    Das Model-API Zur vorigen ÜberschriftZur nächsten Überschrift

In diesem Abschnitt werden Sie das Model-API kennenlernen, mit dem Sie auf die Daten Ihres Models zugreifen können. Das Programm manage.py kann mit dem Parameter shell in einem Shell-Modus gestartet werden, in dem wir unsere Models in einer interaktiven Python-Shell verwenden können.

Anlegen von Datensätzen

Zuerst wollen wir die Shell nutzen, um eine News-Meldung in die Datenbank zu schreiben.

$ python manage.py shell
>>> from news.models import Meldung, Kommentar
>>> from datetime import datetime
>>> m = Meldung(titel='Unsere erste Meldung',
... zeitstempel=datetime.today(),
... text="Klassischerweise steht hier 'Hallo Welt'.")
>>> m.save()

Mit diesem einfachen Code wurde eine neue Meldung erzeugt und auch schon in der Datenbank gespeichert. Da sowohl das Projekt als auch die Applikation einfache Python-Module sind, können wir sie über ein import-Statement einbinden.

Um neue Datensätze zu erzeugen, müssen wir nur die dazugehörige Model-Klasse instanziieren. Der Konstruktor der Model-Klasse erwartet dabei Schlüsselwortparameter für alle Attribute des Datensatzes. Wichtig ist außerdem, dass Django für jeden Spaltenwert einen Wert mit einem Datentyp erwartet, der zu der Spaltendefinition passt. Deshalb muss für den Umgang mit Datumsangaben der Typ datetime.datetime importiert werden. Für die Textspalten sind Strings passend.

Sie können auf die Attribute einer Model-Instanz auf gewohnte Weise zugreifen und sie auch über Zuweisungen verändern.

>>> m.titel
'Unsere erste Meldung'
>>> m.titel = "'Hallo Welt'-Meldung"
>>> m.save()
>>> m.id
1

Mit der letzten Abfrage m.id greifen wir auf die automatisch von Django eingefügte id-Spalte des Models zu. Da es sich bei m um den ersten Eintrag handelt, hat m.id den Wert 1.

Kommentare können wir direkt über die jeweilige Meldung einfügen. Durch die Bindung der Klasse Kommentar an die Meldung bekommt jede Meldung-Instanz ein Attribut kommentar_set, das Zugriff auf die Kommentare der Meldung bietet.

>>> m2 = Meldung(titel='Umfrage zu Django',
... zeitstempel=datetime.today(),
... text='Wie findet ihr das Framework?')
>>> m2.save()
>>> k1 = m2.kommentar_set.create(autor='Heinz', text='Super!')
>>> k2 = m2.kommentar_set.create(autor='Jens', text='Klasse!')
>>> m2.kommentar_set.count()
2
>>> m2.save()>

Nun gibt es eine zweite Meldung in unserer News-Tabelle, die bereits mit zwei Kommentaren versehen ist. Das erste m2.save() ist deshalb erforderlich, da erst beim Speichern ein id-Spaltenwert von der Datenbank erzeugt wird, um Kommentare mit dem Datensatz zu verknüpfen.

Es gibt noch eine unschöne Eigenheit in unserem Model, die wir beseitigen sollten. Schauen Sie sich einmal an, was Python ausgibt, wenn wir eine Meldung-Instanz ausgeben lassen.

>>> m2
<Meldung: Meldung object>

Diese Form der Darstellung ist nicht sehr nützlich, da sie uns keine Informationen über den Inhalt des Objekts liefert. Sie können in der models.py jeder Klasse die Magic Method __str__ angeben, die eine aussagekräftige Repräsentation des Objektinhalts zurückgeben sollte. Wir ändern unsere models.py so ab, dass die __str__-Methoden der Klassen Meldung und Kommentar jeweils das kennzeichnende Attribut text zurückgeben.

class Meldung(models.Model):
...
def __str__(self):
return self.titel

class Kommentar(models.Model):
...
def __str__(self):
return "{} sagt '{}'".format(self.autor, self.text)

Damit die Änderungen auch für die Python-Shell wirksam werden, müssen Sie sie mit python manage.py shell neu starten. Sie beenden dazu einfach den Python-Interpreter mit dem Funktionsaufruf exit() und starten ihn dann erneut. Dabei sollten Sie nicht vergessen, nach dem Neustart auch wieder die Model-Klassen und datetime zu importieren.

Wenn Sie nun in der neuen Shell eine Meldung erzeugen, können Sie sie auch in sinnvoller Weise ausgeben lassen.

>>> m = Meldung(titel='Nun auch mit guten Ausgaben',
... zeitstempel=datetime.today(),
... text='Jetzt sehen die Ausgaben auch gut aus.')
>>> m
<Meldung: Nun auch mit guten Ausgaben>
>>> m.save()

Sie sollten nach Möglichkeit alle Ihre Model-Klassen mit einer __str__-Methode ausstatten, da Django oft darauf zurückgreift, um Informationen zu den Datensätzen auszugeben.

Abfrage von Datensätzen

Mittlerweile wissen Sie, wie man neue Datensätze in die Datenbank eines Django-Projekts einfügt. Genauso wichtig wie das Anlegen neuer Datensätze ist aber auch das Abfragen von Datensätzen aus der Datenbank. Für den Zugriff auf die bereits in der Datenbank vorhandenen Datensätze bietet jede Model-Klasse ein Klassenattribut namens objects an, dessen Methoden ein komfortables Auslesen von Daten ermöglichen.

>>> Meldung.objects.all()
<QuerySet [<Meldung: 'Hallo Welt'-Meldung>,
<Meldung: Umfrage zu Django>,
<Meldung: Nun auch mit guten Ausgaben>]>

Mit der all-Methode von Meldung.objects können wir uns eine Liste[ 213 ](Es handelt sich bei dem Rückgabewert von all nicht um eine Liste im Sinne einer Instanz des Datentyps list. Tatsächlich wird eine Instanz des Django-eigenen Datentyps QuerySet zurückgegeben, der sich nach außen aber ähnlich wie list-Instanzen verhält. ) mit allen Meldungen in der Datenbank zurückgeben lassen. Besonders interessant sind die Methoden get und filter des objects-Attributs, mit denen sich gezielt Datensätze ermitteln lassen, die bestimmte Bedingungen erfüllen. Die gewünschten Bedingungen werden bei den Abfragen als Schlüsselwortparameter übergeben. Wird mehr als eine Bedingung angegeben, verknüpft Django sie automatisch mit einem logischen UND.

Mit get lassen sich einzelne Datensätze abfragen. Sollten die geforderten Bedingungen auf mehr als einen Datensatz zutreffen, wirft get eine MultipleObjectsReturned-Exception. Wird kein passender Datensatz gefunden, quittiert get dies mit einem DoesNotExist-Fehler. Wir nutzen get, um unsere Umfrage-Meldung aus der Datenbank zu lesen.

>>> umfrage = Meldung.objects.get(titel='Umfrage zu Django')
>>> umfrage.kommentar_set.all()
<QuerySet [<Kommentar: Heinz sagt 'Super!'>,
<Kommentar: Jens sagt 'Klasse!'>]>

Wie Sie sehen, liest Django den entsprechenden Datensatz nicht nur aus der Datenbank, sondern erzeugt auch eine passende Instanz der dazugehörigen Model-Klasse, die sich anschließend genauso verwenden lässt, als sei sie gerade erst von uns angelegt worden.

Field Lookups

Mit der Methode filter können wir auch mehrere Datensätze auf einmal auslesen, sofern sie den übergebenen Kriterien entsprechen.

>>> Kommentar.objects.filter(meldung__id=2)
<QuerySet [<Kommentar: Heinz sagt 'Super!'>,
<Kommentar: Jens sagt 'Klasse!'>]>

Bei dieser Abfrage liefert Django alle Kommentar-Datensätze, die mit einer Meldung verknüpft sind, deren id-Attribut den Wert 2 hat. Diese Art der Abfrage ist deshalb möglich, weil Django in der Lage ist, Verknüpfungen auch über mehrere Tabellen hinweg zu »folgen«. Der doppelte Unterstrich wird dabei als Trennung zwischen Objekt und Unterobjekt betrachtet, ähnlich dem Punkt in der Python-Syntax. Diese Art der Bedingungsübergabe wird auch von get unterstützt.

Der doppelte Unterstrich kann neben der Abfrage über die Verknüpfungen von verschiedenen Model-Klassen hinweg auch zur Verfeinerung normaler Bedingungen genutzt werden. Dazu wird einem Schlüsselwortparameter der doppelte Unterstrich, gefolgt von einem speziellen Namen, nachgestellt. Mit dem folgenden filter-Aufruf können Sie beispielsweise alle Umfragen ermitteln, deren text-Attribut mit der Zeichenfolge 'Jetzt' beginnt:

>>> Meldung.objects.filter(text__startswith='Jetzt')
<QuerySet [<Meldung: Nun auch mit guten Ausgaben>]>

Diese Art der verfeinerten Abfrage wird in Django Field Lookup (dt. Feldnachschlagen) genannt. Alle Field Lookups werden in der Form attribut__lookuptyp=wert an die Methode filter übergeben. Django definiert viele und teilweise sehr spezielle Field-Lookup-Typen, weshalb Tabelle 40.3 nur als Einblick zu verstehen ist.

Field-Lookup-Typ Erklärung
exact Prüft, ob das attribut genau gleich wert ist. Dies ist das Standardverhalten, wenn kein Field-Lookup angegeben wird.
contains Prüft, ob attribut den Wert von wert enthält.
gt Prüft, ob attribut größer als wert ist.
(gt: engl. Greater Than)
gte Prüft, ob attribut größer als oder gleich wert ist.
(gte: engl. Greater Than or Equal)
lt Prüft, ob attribut kleiner als wert ist.
(lt: engl. Less Than)
lte Prüft, ob attribut kleiner als oder gleich wert ist.
(lte: engl. Less Than or Equal)
in Prüft, ob attribut in der für wert übergebenen Liste ist, z. B.
Meldung.objects.filter(id__in=[3, 2]).
startswith Prüft, ob der Wert von attribut mit wert beginnt.
endswith Prüft, ob der Wert von attribut mit wert endet.
range Prüft, ob attribut in dem Bereich ist, der vom zweielementigen Tupel wert definiert wird:
Meldung.objects.filter(id__range=(1, 3))
regex Prüft, ob das attribut den regulären Ausdruck wert matcht.
iexact,
icontains,
istartswith,
iendswith,
iregex
Verhalten sich wie der jeweilige Field-Lookup-Typ ohne führendes i, wobei beim Vergleich nicht zwischen Groß- und Kleinschreibung unterschieden wird.

Tabelle 40.3    Eine Übersicht über die wichtigsten Field-Lookup-Typen

Abfragen auf Ergebnismengen

Die filter-Methode gibt eine Instanz des Datentyps QuerySet zurück. Das Besondere an dem Datentyp QuerySet ist, dass man auf seinen Instanzen wiederum die Methoden für den Datenbankzugriff ausführen kann. Auf diese Weise lassen sich sehr komfortabel Teilmengen von Abfrageergebnissen erzeugen.

Beispielhaft ermitteln wir zuerst die Menge aller Kommentare, deren zugehörige Meldung im Titel das Wort 'Umfrage' enthält. Anschließend extrahieren wir aus der Menge die Meldungen, deren Text 'Super!' lautet:

>>> k = Kommentar.objects.filter(meldung__titel__regex='Umfrage zu Django')
>>> k
<QuerySet [<Kommentar: Heinz sagt 'Super!'>,
<Kommentar: Jens sagt 'Klasse!'>]>
>>> k.filter(text='Super!')
<QuerySet [<Kommentar: Heinz sagt 'Super!'>]>

Natürlich können Sie die filter-Methode des ersten Resultats auch direkt aufrufen, ohne die Referenz k anlegen zu müssen.

Kommentar.objects.filter(
meldung__titel__regex='.*Umfrage.*').filter(text='Super!')

Sie sollten sich vor dem Weiterlesen durch »Herumspielen« mit dem Model-API vertraut machen, weil es eine Schlüsselkomponente für den Umgang mit Django darstellt.

 
Zum Seitenanfang

40.4.6    Unser Projekt bekommt ein Gesicht Zur vorigen ÜberschriftZur nächsten Überschrift

Bis hierher ist unser Projekt so weit gediehen, dass eine Datenbank mit News-Meldungen und Kommentaren erstellt worden ist. In diesem Abschnitt werden wir uns mit dem »Gesicht« unserer Seite beschäftigen, also dem, was der Besucher der Website zu sehen bekommen soll.

Sie erinnern sich sicherlich noch an das Schema zum Aufbau der Model-View-Architektur im Abschnitt zur Einführung von Django.

Den linken Teil kennen wir nun.

Abbildung 40.6    Den linken Teil kennen wir nun.

Bildlich gesprochen, haben wir uns von links nach rechts genau bis zur Mitte vorgearbeitet, da wir uns bereits mit Modellen und Datenbanken, nicht aber mit den Views und der Benutzerschnittstelle beschäftigt haben. Wir werden uns nun um die rechte Seite des Schemas kümmern.

Views

Eine View ist eine einfache Python-Funktion, die das zurückgibt, was im Browser des Besuchers angezeigt werden soll. Welche Art von Daten das genau ist, kann frei gewählt werden. Eine View kann beispielsweise HTML-Quelltext zurückgeben, aber auch einfacher Text oder sogar Binärdateien wie Bilder oder PDF-Dateien sind möglich. In der Regel werden Sie für alles eine eigene View definieren, was Sie auf einer Website anzeigen wollen.

Unser Beispielprojekt wird zwei Views haben: eine zum Anzeigen einer Meldungsübersicht und eine für die Ansicht einer einzelnen Meldung inklusive ihrer Kommentare. Die View-Funktion für die Meldungsübersicht bekommt von uns den Namen meldungen, und die andere nennen wir meldungen_detail.

Da wir uns in diesem Abschnitt auf den prinzipiellen Umgang mit Views konzentrieren möchten, werden wir eine normale Textausgabe verwenden. Wie Sie auch komfortabel HTML-Quellcode erzeugen können, zeigen wir dann im nächsten Abschnitt.

Die Views einer Applikation werden üblicherweise in der Datei views.py abgelegt. Eine views.py, die eine einfache View-Funktion meldungen für die Textausgabe unserer Meldungen enthält, sieht folgendermaßen aus:

from django.shortcuts import render
from django.http import HttpResponse

from .models import Meldung, Kommentar

def meldungen(request):
zeilen = []
for m in Meldung.objects.all():
zeilen.append("Meldung: '{}' vom {}".format(
m.titel, m.zeitstempel.strftime('%d.%m.%Y um %H:%M')))
zeilen.append('Text: {}'.format(m.text))
zeilen += ['', '-' * 30, '']
antwort = HttpResponse('\n'.join(zeilen))
antwort['Content-Type'] = 'text/plain'
return antwort

Am Anfang binden wir per import die beiden Model-Klassen aus der news-Applikation des Projekts ein. Anschließend importieren wir eine Klasse namens HttpResponse aus dem Modul django.http, die wir in unseren Views benutzen, um das Ergebnis zurückzugeben.

Die View-Funktion meldungen bekommt von Django einen Parameter namens request übergeben, mit dem wir auf bestimmte Informationen der Abfrage zugreifen können. Wir werden request erst im nächsten Abschnitt benötigen.

Innerhalb von meldungen verwalten wir eine Liste namens zeilen, die alle Textzeilen des Ergebnisses speichert. In einer for-Schleife iterieren wir über alle Meldungen in der Datenbank, die wir mit der Model-API auslesen, und fügen jeweils fünf Zeilen für jede Meldung in die Liste zeilen ein.

Am Ende erstellen wir eine Instanz des Datentyps HttpResponse, dessen Konstruktor wir die Verkettung der Zeilen als Parameter übergeben. Wichtig ist, dass wir über den []-Operator den 'Content-Type' (dt. Inhaltstyp) der Ausgabe mit 'text/plain' auf einfachen Text setzen, weil es sonst im Browser zu Darstellungsproblemen kommt.[ 214 ](Mit dem []-Operator von HttpResponse-Instanzen können beliebige HTTP-Kopfdaten gesetzt werden. )

Bevor wir uns das Ergebnis unserer Bemühungen im Browser ansehen können, müssen wir Django mitteilen, unter welcher Adresse die View zu erreichen sein soll.

Adressen definieren

Wenn Sie schon selbst Webseiten erstellt haben, sind Sie es wahrscheinlich gewohnt, dass Sie Ihre Programme (beispielsweise index.php) und andere Dateien (wie beispielsweise Bilddateien) direkt über deren Adresse auf dem Server ansprechen können. Liegt beispielsweise eine Datei index.php im Verzeichnis meine_seite/scripts/ relativ zum Wurzelverzeichnis des Webservers unter der Adresse http://www.server.de, können Sie sie über die Adresse http://www.server.de/meine_seite/scripts/index.php ansprechen.

Django geht einen anderen Weg, indem es vollständig von der Ordnerstruktur des Servers abstrahiert. Anstatt die Adressen der realen Dateien auf dem Server für den öffentlichen Zugang zu übernehmen, können Sie selbst angeben, über welche Adresse ein bestimmter Teil der Seite erreichbar sein soll. Dabei ist jede View einzeln ansprechbar und kann mit einer beliebigen Adresse verknüpft werden.

Die Konfiguration der Adressen erfolgt über Regular Expressions[ 215 ](Details zu Regular Expressions (dt. regulären Ausdrücken) können Sie in Kapitel 28 nachlesen. ) in Konfigurationsdateien mit dem Namen urls.py. Dabei besitzen in der Regel das Projekt selbst sowie jede der Applikationen eine eigene urls.py. In einer solchen Datei wird eine Variable namens urlpatterns definiert, die eine Liste mit allen Adressen des Projekts enthält. Die Adressangaben selbst sind Tupel mit zwei Elementen, wobei das erste Element den regulären Ausdruck für die Adresse und das zweite Element die Informationen zu der verknüpften View enthält. Die Datei news_seite/urls.py für unser Projekt, die auch schon eine Adressangabe für unsere meldungen-View enthält, sieht folgendermaßen aus:

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
url(r'^meldungen/', include('news.urls', namespace='news')),
url(r'^admin/', admin.site.urls),
]

Für den Moment ist für uns nur die Zeile

    url(r'^meldungen/', include('news.urls', namespace='news')) 

von Interesse. Hier legen wir fest, dass eine Adresse, die nach der Serveradresse mit 'meldungen/' beginnt,[ 216 ](Ein führender Zirkumflex sorgt dafür, dass der reguläre Ausdruck am Anfang des Strings stehen muss, um zu passen. ) im Modul news.urls weiterverarbeitet werden soll. Dieses Modul befindet sich in der Datei news/urls.py, die wir mit folgendem Inhalt füllen:

from django.conf.urls import url
from . import views

urlpatterns = [
url(r'^$', views.meldungen, name='meldungen'),
]

Die Regel zum regulären Ausdruck r'^$' legt nun fest, dass eine leere Adresse das Ergebnis unserer zuvor definierten Funktion meldungen liefern soll.

Rufen wir nun in unserem Browser die Adresse http://127.0.0.1:8000/meldungen/ auf, erscheint tatsächlich eine Übersicht der Meldungen, wie in Abbildung 40.7 gezeigt.[ 217 ](Falls noch nicht geschehen, müssen Sie den Entwicklungswebserver von Django mit python manage.py runserver starten. )

Unsere erste eigene Django-Seite im Browser

Abbildung 40.7    Unsere erste eigene Django-Seite im Browser

Die Auflösung der Adresse ist dabei nach folgendem Schema erfolgt:

Der Webserver wurde nach der Adresse 'http://127.0.0.1:8000/meldungen/' gefragt, die sich aus der Serveradresse, also 'http://127.0.0.1:8000/', und dem Pfad auf dem Server, hier 'meldungen/', zusammensetzt. Für die Auflösung wird in der urls.py im Projektverzeichnis nach einer Regel gesucht, die auf den Pfad 'meldungen/' passt. In unserem Fall ist das die erste Regel aus der folgenden Zeile:

url(r'^meldungen/', include('news.urls', namespace='news')),

Anschließend entfernt Django den zur Regel gehörenden Teil der relativen Adresse und sucht für den Rest eine passende Regel im Modul 'news.urls'. Weil nach dem Entfernen von 'meldungen/' aus der relativen Adresse nichts mehr übrig bleibt, passt nun die Regel aus der Datei news/urls.py, die mit r'^$' nach dem leeren String sucht.

Durch dieses Entfernen übergeordneter Adressteile können Applikationen mit relativen Adressen arbeiten, ohne die übergeordnete Struktur kennen zu müssen.

[»]  Hinweis

Die Parameterwerte für namespace und name sind im Moment noch unwichtig. Sie werden später im Zusammenhang mit der Erzeugung von Adressen verwendet.

Parametrisierte Adressen

Wir wollen nun auch die Detailseite jeder Meldung für den Benutzer zugänglich machen. Dabei wäre es äußerst unschön, für jede Meldung eine eigene View-Funktion zu definieren, da wir einerseits den Programm-Code aufblähen und andererseits die Anzahl möglicher Meldungen dadurch begrenzen würden, wie viele View-Funktionen wir implementieren.

Wesentlich eleganter ist es, wenn wir eine View-Funktion definieren, die jede beliebige Meldung darstellen kann. Welche Meldung konkret angefordert wird, soll dabei über einen Parameter festgelegt werden, der die id der gewünschten Meldung enthält.

Django unterstützt die Parameterübergabe für Views über sogenannte benannte Gruppen, die in Abschnitt 28.1.7 über reguläre Ausdrücke behandelt werden. Mit benannten Gruppen können wir Teile aus einem String extrahieren und ihnen Namen geben, wenn ein bestimmter regulärer Ausdruck auf den String passt.

Um die einzelnen Meldungen über Adressen wie http://www.server.de/meldungen/1/ und http://www.server.de/meldungen/2/ erreichbar zu machen, ergänzen wir in der Datei news/urls.py folgenden Eintrag:

url(r'^(?P<meldungs_id>\d+)/$', views.meldungen_detail,
name='meldungen_detail'),

Wenn nun ein Besucher der Seite auf die Adresse http://www.server.de/meldungen/2/ zugreift, findet Django zunächst den passenden Eintrag r'^meldungen/' in der Datei news_seite/urls.py. Nachdem anschließend der Anfang 'meldungen/' abgeschnitten wurde, bleibt noch der String '2/' übrig, wofür Django dann in der Datei news/urls.py eine passende Regel sucht. Diese wird mit dem regulären Ausdruck r'^(?P<meldungs_id>\d+)/$' gefunden, wobei der String '2' von der benannten Gruppe mit dem Namen meldungs_id gematcht wird.

Nun ruft Django die View-Funktion news.views.meldungen_detail auf, wobei zusätzlich zum request-Parameter ein Schlüsselwortparameter meldungs_id mit dem Wert '2' übergeben wird.

Eine URL-Regel kann durchaus mehrere benannte Gruppen umfassen, wie das folgende Beispiel zeigt:

url(r'^(?P<a>\d+)/(?P<b>\w+)/(?P<c>\d+)$', views.view_fkt, name='bsp')

Eine Anfrage für die Adresse http://www.server.de/meldungen/99/waldkauz/2017/ führt mit dieser Regel zu dem Aufruf view.view_fkt(request, a='99', b='waldkauz', c='2017'). Dabei haben wir vorausgesetzt, dass sich die Regel in der Datei news/urls.py befindet.

Alternativ zu benannten Gruppen können auch unbenannte Gruppen in den regulären Ausdrücken der Adressspezifikation verwendet werden. Im Unterschied zu den benannten Gruppen werden die Werte unbenannter Gruppen als Positionsparameter an die View-Funktion übergeben. Wir betrachten das folgende Beispiel:

url(r'^(\d+)/(\w+)/(\d+)$', views.view_fkt, name='bsp2')

Die Anfrage für die oben genannte Adresse http://www.server.de/meldungen/99/waldkauz/2017/ führt dann zu dem folgenden Aufruf:

view.view_fkt(request, '99', 'waldkauz', '2017')

Die View-Funktion meldungen_detail

Nun implementieren wir die View-Funktion meldungen_detail, indem wir Folgendes in die Datei views.py schreiben:

from django.http import HttpResponse, Http404
from .models import Meldung, Kommentar

def meldungen(request):
...

def meldungen_detail(request, meldungs_id):
try:
m = Meldung.objects.get(id=meldungs_id)
except Meldung.DoesNotExist:
raise Http404

zeilen = [
"Titel: '{}' vom {}".format(
m.titel, m.zeitstempel.strftime('%d.%m.%Y um %H:%M')),
'Text: {}'.format(m.text),
'', '-' * 30,
'Kommentare:', '']
zeilen += ['{}: {}'.format(k.autor, k.text)
for k in m.kommentar_set.all()]
antwort = HttpResponse('\n'.join(zeilen))
antwort['Content-Type'] = 'text/plain'
return antwort

Wir importieren zusätzlich die Exception Http404, um einen Fehler an den Browser des Besuchers zu senden, falls er eine nicht vorhandene Meldung aufruft. Als Wert für den Parameter meldungs_id bekommen wir bei jedem Aufruf der View den Wert übergeben, der in der Adresse angegeben wurde. In einer try/except-Anweisung versuchen wir, die passende Meldung auszugeben, und erzeugen bei Misserfolg den oben genannten Http404-Fehler.

Konnte die Meldung erfolgreich aus der Datenbank gelesen werden, speichern wir die Textzeilen für die Benutzerseite in der Liste zeilen und erzeugen außerdem mittels einer List Comprehension Ausgaben für alle Kommentare der Meldung.

Schließlich verpacken wir das Ganze auf gewohnte Weise in einer HttpResponse-Instanz, die wir per return zurückgeben.

Die von meldungen_detail erzeugte Ausgabe sieht nun beispielsweise so aus wie in Abbildung 40.8.

Beispiel einer Meldungsdetailseite

Abbildung 40.8    Beispiel einer Meldungsdetailseite

Shortcut-Funktionen

Wenn Sie Webanwendungen entwickeln, werden sich in Ihrem Code sehr oft ähnliche Strukturen wiederfinden. Beispielsweise ist es sehr gängig, einen Datensatz aus der Datenbank abzurufen und, wenn dieser nicht existiert, einen Http404-Fehler zu erzeugen.

Damit Sie nicht jedes Mal den gleichen Code eintippen müssen und dadurch Ihren Programmtext künstlich aufblasen, bietet Django sogenannte Shortcut-Funktionen an, die häufig benötigte Aufgaben für Sie übernehmen. Die Shortcut-Funktionen befinden sich im Modul django.shortcuts und können per import eingebunden werden.

Um beispielsweise einen Datensatz aus der Datenbank abzufragen und bei Misserfolg eine Http404-Exception zu werfen, verwenden Sie die Shortcut-Funktion get_object_or_404. Die Funktion get_object_or_404 hat fast die gleiche Schnittstelle wie die get-Methode der Model-API, mit der einzigen Ausnahme, dass als erster Parameter die Model-Klasse des gesuchten Datensatzes übergeben werden muss.

Damit wird aus der try/except-Anweisung eine einzeilige Zuweisung (vergessen Sie nicht, get_object_or_404 aus django.shortcuts zu importieren):

m = get_object_or_404(Meldung, id=meldungs_id)

Django definiert eine Reihe weiterer Shortcut-Funktionen, die wir hier nicht thematisieren werden. In der Dokumentation zu Django finden Sie eine ausführliche Beschreibung aller verfügbaren Shortcuts.

 
Zum Seitenanfang

40.4.7    Djangos Template-System Zur vorigen ÜberschriftZur nächsten Überschrift

Unsere bisher implementierten Views sind noch alles andere als optimal: Erstens sind sie optisch wenig ansprechend, da nur einfacher Text ausgegeben wird, und außerdem werden sie direkt aus String-Konstanten in der View-Funktion erzeugt. Besonders im zweiten Punkt muss noch nachgebessert werden, da es eines der Hauptziele von Django ist, die Komponenten eines Projekts möglichst unabhängig voneinander zu gestalten. Im Optimalfall kümmert sich die View-Funktion nur um die Verarbeitung der Parameter und die Abfrage und Aufbereitung der Daten. Die Erzeugung der Ausgabe für den Browser sollte einem anderen System übertragen werden, das sich wirklich nur um die Ausgabe kümmert.

Hier kommen sogenannte Templates (dt. Schablonen) ins Spiel, die darauf spezialisiert sind, aus übergebenen Daten ansprechende Ausgaben zu generieren. Im Prinzip handelt es sich bei Templates um Dateien, die Platzhalter enthalten. Wird ein Template mit bestimmten Werten für die Platzhalter aufgerufen, werden die Platzhalter durch eben diese Werte ersetzt, und als Ergebnis enthält man die gewünschte Ausgabe. Neben einfachen Ersetzungen von Platzhaltern unterstützt das Template-System von Django auch Kontrollstrukturen wie Fallunterscheidungen und Schleifen.

Bevor wir uns mit der Definition von Templates selbst beschäftigen, werden wir das Einbinden von Templates in View-Funktionen besprechen.

Django kapselt sein Template-System in dem Untermodul django.template. Mit der Klasse loader dieses Moduls können wir eine Template-Datei laden und daraus ein neues Template-Objekt erzeugen. Die Werte für die Platzhalter in dem Template werden über einen sogenannten Kontext übergeben, der über die Klasse RequestContext erzeugt werden kann.

Einbinden von Templates in View-Funktionen

In Django wird ein Template durch eine Datei repräsentiert. Dabei ist es wichtig, wo diese Datei abgelegt wird, damit Djangos Template-System sie finden kann. Standardmäßig sucht Django in Unterverzeichnissen namens template in allen Applikationsverzeichnissen. Beispielsweise sollten Templates zur Applikation news in dem Verzeichnis news_seite/news/templates/news abgelegt werden.

Das Template für die Übersichtsseite unserer News-Applikation speichern wir daher unter dem Pfad news_seite/news/templates/news/meldungen.html ab.

Bevor wir das Template mit Inhalt füllen, passen wir die View-Funktion meldungen so an, dass sie das neue Template verwendet.

from django.http import HttpResponse
from django.template import RequestContext, loader

from .models import Meldung, Kommentar

def meldungen(request):
template = loader.get_template('news/meldungen.html')
context = RequestContext(request,
{'meldungen' : Meldung.objects.all()})
return HttpResponse(template.render(context))

Durch diese Anpassung ist die View-Funktion meldungen im Wesentlichen auf drei Zeilen geschrumpft.[ 218 ](Man kann sich auf den Standpunkt stellen, dass so einfach gestrickte View-Funktionen überflüssig sind. Django bietet dafür sogenannte Generic Views (dt. allgemeine Ansichten) an. Näheres dazu erfahren Sie in der Django-Dokumentation. )

Mit der get_template-Methode der loader-Klasse laden wir das gewünschte Template. Dann erzeugen wir einen Kontext, der die Liste aller Meldungen mit dem Platzhalter 'meldungen' verknüpft. Die endgültige Ausgabe des Templates für den erzeugten Kontext generieren wir mit der render-Methode und übergeben das Ganze als Parameter an HttpResponse. Die Änderung des Kontexttyps nach 'text/plain' entfällt, da unsere Templates im folgenden HTML-Code erzeugen werden.[ 219 ](Natürlich können Sie auch Templates schreiben, die weiterhin reine Textausgaben erzeugen. )

Nun können wir uns mit dem Template meldungen.html selbst befassen.

[»]  Hinweis

Sie wundern sich wahrscheinlich, warum wir innerhalb des Verzeichnisses news/templates ein weiteres Verzeichnis news angelegt haben, anstatt das Template direkt in news/templates zu speichern.

Diese Ablagestrategie verhindert, dass Mehrdeutigkeiten entstehen, wenn Sie mehrere Applikationen in einem Projekt verwenden, die gleichnamige Templates haben. Nehmen wir einmal an, unser Projekt hätte eine weitere Applikation staus, die Informationen über den aktuellen Verkehr bereitstellt und auch eine Template-Datei mit dem Namen meldungen.html verwendet. In diesem Fall ist der Name 'meldungen.html' zur Identifizierung nicht mehr eindeutig, während die Namen 'news/meldungen.html' und 'staus/meldungen.html' es weiterhin sind.

Die Template-Sprache von Django

Django implementiert für die Definition von Templates eine eigene Sprache. Diese ist so ausgelegt, dass damit jeder beliebige Ausgabedatentyp erzeugt werden kann, solange er sich als Text ausdrücken lässt. Es bleibt also Ihnen überlassen, ob Sie einfachen Text, HTML-Quelltext, XML-Dokumente oder andere textbasierte Dateitypen generieren.

Das Template meldungen.html enthält in unserem Beispiel folgenden Template-Code:[ 220 ](Bitte beachten Sie, dass hier bewusst wegen der Übersichtlichkeit auf wichtige HTML-Elemente verzichtet wurde, wodurch der HTML-Code nicht mehr den Standards entspricht. Sie sollten natürlich in Ihren eigenen Programmen nur gültige HTML-Dateien erzeugen. )

<h1>News-&Uuml;bersicht</h1>

{% for m in meldungen %}
<div class="container">
<div class="titelzeile">
<div class="titel">{{ m.titel|escape }}</div>
<div class="zeitstempel">
{{ m.zeitstempel|date:'Y.m.d' }} um
{{ m.zeitstempel|time:'H:i' }} Uhr
</div>
<div style="clear: both"></div>
</div>
<div class="text">
{{ m.text|escape|linebreaksbr }}
<div class="link_unten">
<a href="{% url 'news:meldungen_detail' m.id %}">Details</a>
</div>
</div>
</div>
{% endfor %}

Im Prinzip ist das oben dargestellte Template eine einfache HTML-Datei, die durch spezielle Anweisungen der Template-Sprache ergänzt wird. In dem Beispiel wurden alle Stellen fett gedruckt, an denen Djangos Template-Sprache zum Einsatz kommt.

Wir werden nun die markierten Stellen unter die Lupe nehmen.

Variablen ausgeben

Die Ausgabe von Elementen des verwendeten Kontextes erfolgt über doppelte geschweifte Klammern. Mit {{ meldungen }} wird dabei beispielsweise die Kontextvariable meldungen ausgegeben, und mit {{ m.titel }} wird auf das Attribut titel der Kontextvariablen n zugegriffen. Wie Sie sehen, kann bei der Ausgabe auch der Punkt verwendet werden, um auf die Attribute von Kontextvariablen zuzugreifen.

Der Zugriff auf Attribute ist aber nur eine spezielle Anwendung des Punktoperators in Djangos Template-Sprache. Wenn Django bei der Verarbeitung eines Templates auf eine Angabe wie {{ variable.member }} stößt, ermittelt es die Daten in folgender Weise:

  1. Zuerst wird versucht, wie bei einem Dictionary mit variable['member'] einen Wert zu finden.
  2. Schlägt dies fehl, wird versucht, den Wert mit variable.member auszulesen.
  3. Bei erneutem Fehlschlag wird probiert, member als einen Listenindex zu interpretieren, indem mit variable[member] ein Wert gelesen wird. (Dies geht natürlich nur, wenn member eine Ganzzahl ist.)
  4. Wenn alle diese Versuche scheitern, nimmt Django den Wert, der in der settings.py unter TEMPLATES['OPTIONS']['string_if_invalid'] gesetzt wurde. Standardmäßig ist dies ein leerer String.

Aufgrund dieses Vorgehens ist z. B. auch der als Python-Code ungültige Ausdruck {{ meldungen.0 }} zulässig, um auf das erste Element der Kontextvariablen meldungen zuzugreifen.

Ergibt sich bei der Auswertung nach dem oben dargestellten Schema eine aufrufbare Instanz, wird sie aufgerufen und ihr Rückgabewert ausgegeben. Hat die Kontextvariable variable beispielsweise eine Methode irgendwas, wird ihr Rückgabewert durch {{ variable.irgendwas }} im Template ausgegeben.

Filter für Variablen

Sie können das Ersetzen von Kontextvariablen durch sogenannte Filter anpassen. Ein Filter ist eine Funktion, die einen String verarbeiten kann, und wird so verwendet, dass man der Variablen bei ihrer Ausgabe einen senkrechten Strich, gefolgt vom Filternamen, nachstellt.

{{ variable|filter }}

Es ist auch möglich, mehrere Filter hintereinanderzuschalten, indem sie durch einen senkrechten Strich getrennt hintereinandergeschrieben werden.

{{ variable|filter1|filter2|filter3 }}

In dem Beispiel würde zuerst filter1 auf den Wert von variable angewandt, das Ergebnis an filter2 übergeben und dessen Rückgabewert schließlich mit filter3 verarbeitet.

Manchen Filtern können auch Parameter übergeben werden. Dazu wird dem Filternamen ein Doppelpunkt gefolgt, von einem String mit dem Parameter, nachgestellt.

{{ variable|filter:'parameter'}}

Im Beispiel wurde dies angewandt, um die Ausgabe des Zeitstempels anzupassen.[ 221 ](Das Format für Datums- und Zeitformatierungen ist an die PHP-Funktion date angelehnt und unterscheidet sich daher vom Format der Funktion strftime im Modul time der Standardbibliothek. Für nähere Informationen verweisen wir Sie auf die Dokumentation unter: https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#std:templatefilter-date)

Django implementiert eine ganze Reihe solcher Filter. In Tabelle 40.4 sind die Filter erklärt, die in unserem Beispiel Verwendung finden.

Filter Bedeutung
Escape Ersetzt die Zeichen <, >, &, " und ' durch entsprechende HTML-Codierungen.
linebreaksbr Ersetzt alle Zeilenvorschübe durch das HTML-Tag <br/>, das eine neue Zeile erzeugt.
Date Formatiert das Datum mit dem Format, das der übergebene Parameter festlegt.
Time Formatiert die Zeit mit dem Format, das der übergebene Parameter festlegt.

Tabelle 40.4    Einige Filter von Django

Im Übrigen ist es auch möglich, eigene Filter zu definieren. Informationen dazu und eine ausführliche Übersicht mit allen Django-Filtern finden Sie in der Dokumentation.

Tags

Djangos Template-Sprache arbeitet mit sogenannten Tags (dt. Kennzeichnungen), mit denen Sie den Kontrollfluss innerhalb eines Templates steuern können. Jedes Tag hat die Form {% tag_bezeichnung %}, wobei tag_bezeichnung vom jeweiligen Tag abhängt.

Es gibt auch Tags, die einen Block umschließen. Solche Tags haben die folgende Struktur:

{% tag_bezeichnung parameter %}
Inhalt des Tags
{% endtag_bezeichnung %}

Es gibt Tags, mit denen sich Kontrollstrukturen wie die bedingte Ausgabe oder die wiederholte Ausgabe eines Blocks abbilden lassen.

Der if-Block dient dazu, einen bestimmten Teil des Templates nur dann auszugeben, wenn eine Bedingung erfüllt ist.

{% if besucher.hat_geburtstag % }
Willkommen und herzlichen Glückwunsch zum Geburtstag!
{% else %}
Willkommen auf unserer Seite!
{% endif %}

Wenn besucher.hat_geburtstag den Wahrheitswert True ergibt, wird dem Besucher der Seite zum Geburtstag gratuliert. Ansonsten wird er normal begrüßt, was über den else-Zweig festgelegt wird. Natürlich kann der else-Zweig auch entfallen.

Als Bedingung können auch komplexe logische Ausdrücke gebildet werden:

{% if bedingung1 and bedingung2 or bedingung3 %}
Es gelten bedingung1 und bedingung2 und/oder es gilt
bedingung3
{% endif %}

Neben den Fallunterscheidungen gibt es auch ein Äquivalent zu Python-Schleifen: das for-Tag. Das for-Tag ist dabei eng an die Syntax von Python angelehnt und kann beispielsweise folgendermaßen verwendet werden:

{% for name in namen %}
{{ name }} ist ein toller Name
{% endfor %}

Diese Schleife funktioniert natürlich nur dann, wenn die Kontextvariable namen auf ein iterierbares Objekt verweist.

Hätte name den Wert ['Constantin', 'Lothar', 'Cathy'], würde das oben dargestellte Template folgende Ausgabe produzieren:

Constantin ist ein toller Name
Lothar ist ein toller Name.
Cathy ist ein toller Name.
Adressen erzeugen

Django ist in der Lage, Adressen auf die Seiten Ihres Projekts automatisch zu erzeugen. Dazu dient das Tag url, das wir im Beispiel verwendet haben, um Links auf die Detailseiten der Meldungen zu erzeugen.

{% url 'news:meldungen_detail' m.id %}

Die Angabe 'news:meldungen_detail' identifiziert dabei die aufzurufende Seite nach dem Schema <Namensraum>:<View-Name>. In Abschnitt 40.4.6 unter »Adressen definieren« haben wir in der Datei news_seite/urls.py alle Regeln der Datei news/urls.py durch namespace='news' mit dem Namensraum 'news' verknüpft. Also sucht Django dort nach einer Regel mit dem Namen 'meldungen_detail'. Außerdem haben wir eine entsprechende Regel definiert, die einen Parameter für die id der Meldung erwartet. Dadurch ist Django in der Lage, die passende Adresse zu erzeugen, wobei der Wert m.id für den Parameter meldungs_id verwendet wird.

Wenn Sie diesen Mechanismus verwenden, müssen Sie in Templates die Adressen zu Unterseiten des Projekts nicht explizit angeben, sondern Sie können die Namen der entsprechenden URL-Regeln verwenden. Um die Adressierung in Ihrem Projekt nachträglich zu verändern, können Sie deshalb zentral die Regeln in den urls.py-Dateien anpassen, ohne alle Templates bearbeiten zu müssen.

Vererbung bei Templates

Es kommt häufig vor, dass viele Seiten einer Webanwendung das gleiche Grundgerüst wie beispielsweise eine Kopfzeile oder Navigation besitzen. Wenn aber jede Seite ein eigenes Template hat, müsste dieses Grundgerüst in allen Templates enthalten sein, was zu unnötigen Code-Dopplungen führt.

Um dieses Problem zu lösen, können Sie das Grundgerüst der Seite in einem zentralen Template definieren und von diesem die konkret benötigten Templates ableiten.

Angenommen, das Template in der Datei basis.html enthält das Grundgerüst der Webseite, kann mithilfe des extends-Tags ein anderes Template davon abgeleitet werden:

{% extends 'basis.html' %}

Dies hat zur Folge, dass der komplette Inhalt von basis.html in das erbende Template eingefügt wird. Damit ein erbendes Template auch den Inhalt der entstehenden Seite selbst bestimmen kann, kann ein Template sogenannte Blöcke mit dem block-Tag definieren.

Ein Block ist dabei eine Stelle innerhalb eines Templates, die mit einem Namen versehen wird und durch erbende Templates mit konkretem Inhalt versehen werden kann.

Betrachten wir zwei Beispieldateien:

basis.html

---------- Kopfzeile ------------
{% block inhalt %}Standardinhalt{% endblock %}
---------- Fußzeile -------------

Wenn Sie dieses Template mit Django ausgeben lassen, wird das block-Tag einfach ignoriert und durch seinen Inhalt ersetzt.

---------- Kopfzeile ------------
Standardinhalt
---------- Fußzeile -------------

Interessant wird es dann, wenn wir ein anderes Template von basis.html erben lassen.

erbendes_template.html

{% extends 'basis.html' %}
{% block inhalt %}Hallo, ich habe geerbt!{% endblock}

Die Ausgabe von erbendes_template.html sieht dann so aus:

---------- Kopfzeile ------------
Hallo, ich habe geerbt!
---------- Fußzeile -------------

Natürlich ist ein Grundgerüst einer Seite nicht die einzige Anwendung für die Template-Vererbung. Sie können Vererbung immer dann einsetzen, wenn mehrere Templates auf einer gemeinsamen Struktur basieren.

Mit diesem Wissen können wir nun ein ansprechendes HTML-Template-Gefüge für unser Webprojekt erstellen. In einer Datei basis.html werden wir das Grundgerüst der Seite mitsamt den CSS-Stylesheets[ 222 ](Cascading Style Sheets (CSS) ist eine Formatierungssprache, um beispielsweise die Ausgabe von HTML-Seiten anzupassen. ) ablegen. Die Datei basis.html hat den folgenden Inhalt, wobei aus Gründen der Übersichtlichkeit auf die Angabe einiger Teile verzichtet wurde.

<!DOCTYPE html>
<html>
<head>
<title>Unsere Django-Seite</title>
<style type="text/css">
    /* Hier kommen die CSS-Styles hin */
  </style>
</head>
<body>
<div id="inhalt">
<h2>{% block titel %}Django Beispielseite{% endblock %}</h2>
{% block inhalt %}
{% endblock %}
</div>
</body>
</html>

Das Template definiert eine einfache HTML-Seite, in der es zwei Template-Blöcke gibt: titel und inhalt. Diese Blöcke sollen nun von den Templates für die Meldungsübersicht und die Meldungsdetails mit Inhalt gefüllt werden.

Wir gehen in unserem Beispiel davon aus, dass unser Webprojekt news_seite aus mehreren Applikationen bestehen wird, die sich alle dasselbe Grundgerüst teilen. Deshalb ist es sinnvoll, die Template-Datei basis.html an einem zentralen Ort abzulegen, sodass alle Applikationen darauf zugreifen können. Dazu erstellen wir im Verzeichnis news_seite/news_seite ein Unterverzeichnis templates. Genau wie vorher bei Templates zur Applikation news legen wir in diesem Template-Verzeichnis ein Unterverzeichnis main an, in dem die zentralen Templates des Projekts wie z. B. basis.html abgelegt werden. Die entstehende Verzeichnisstruktur sehen Sie in Abbildung 40.9.

mit Main-Template

Abbildung 40.9    mit Main-Template

Jetzt müssen wir noch dafür sorgen, dass Django das Template main/basis.html in unserem zentralen Template-Verzeichnis findet. Dazu passen wir die Einstellung TEMPLATES in der Datei settings.py folgendermaßen an:

TEMPLATES = [
{
# Andere Einstellungen
'DIRS': [os.path.join(BASE_DIR, 'news_seite', 'templates')],
# Noch mehr andere Einstellungen
},
]

Nun verändern wir die Datei meldungen.html der news-Applikation so, dass sie von main/basis.html erbt und die Blöcke titel und inhalt füllt.

{% extends 'main/basis.html' %}
{% block titel %}News-&Uuml;bersicht{% endblock %}
{% block inhalt %}
{% for m in meldungen %}
<div class="container">
<div class="titelzeile">
<div class="titel">{{ m.titel|escape }}</div>
<div class="zeitstempel">
{{ m.zeitstempel|date:'Y.m.d' }} um
{{ m.zeitstempel|time:'H:i' }} Uhr
</div>
<div style="clear: both"></div>
</div>
<div class="text">
{{ m.text|escape|linebreaksbr }}
<div class="link_unten">
<a href="{% url 'news:meldungen_detail' m.id %}">Details</a>
</div>
</div>
</div>
{% endfor %}
{% endblock %}

Wenn Sie die Dateien gespeichert haben, können Sie das Ergebnis in Ihrem Browser betrachten[ 223 ](Die vollständige basis.html-Datei inklusive aller CSS-Styles finden Sie im Onlineangebot zu diesem Buch (www.rheinwerk-verlag.de/4467). ) (siehe Abbildung 40.10).

Schicke HTML-Ausgabe unseres ersten Templates

Abbildung 40.10    Schicke HTML-Ausgabe unseres ersten Templates

Wenn Sie auf dieser Seite den Details-Link anklicken, gelangen Sie natürlich weiterhin zu der tristen Textansicht der jeweiligen Meldung. Um dies zu ändern, passen wir auch die View-Funktion meldungen_detail an.

from django.http import HttpResponse, Http404
from django.shortcuts import render, get_object_or_404
from django.template import RequestContext, loader

from .models import Meldung, Kommentar

def meldungen(request):
...

def meldungen_detail(request, meldungs_id):
template = loader.get_template('news/meldungen_detail.html')
meldung = get_object_or_404(Meldung, id=meldungs_id)
kontext = RequestContext(request, {'meldung' : meldung})
return HttpResponse(template.render(kontext))

Beachten Sie den neu hinzugekommenen Import von get_object_or_404.

Um unsere HTML-Ausgabe zu komplettieren, fehlt nur noch das Template für die Detailseite unserer Meldungen, das wir im selben Verzeichnis wie meldungen.html unter dem Namen meldungen_detail.html ablegen.

{% extends 'main/basis.html' %}
{% block titel %}
News-Details f&uuml;r Eintrag {{ meldung.id }}
{% endblock %}
{% block inhalt %}
<div class="container">
<div class="titelzeile">
<div class="titel">{{ meldung.titel|escape }}</div>
<div class="zeitstempel">{{ meldung.zeitstempel }}</div>
<div style="clear: both"></div>
</div>
<div class="text">
{{ meldung.text|escape|linebreaksbr }}
</div>
</div>
<div class="container">
<div class="titelzeile">Kommentare</div>
{% if meldung.kommentar_set.count %}
<table>
{% for k in meldung.kommentar_set.all %}
<tr class="kommentarzeile">
<td class="spaltenbezeichner">{{ k.autor }}:</td>
<td>{{ k.text|escape|linebreaksbr }}</td>
</tr>
{% endfor %}
</table>
{% else %}
Keine Kommentare
{% endif %}
</div>
<div class="link_unten">
<a href="{% url 'news:meldungen' %}">Zur&uuml;ck</a>
</div>
{% endblock %}

Im Browser stellt sich das Ganze dann so dar wie in Abbildung 40.11.

Detailseite einer Meldung mit zwei Kommentaren

Abbildung 40.11    Detailseite einer Meldung mit zwei Kommentaren

Ihnen wird sicherlich aufgefallen sein, dass sich die beiden Views meldungen und meldungen_detail strukturell sehr stark ähneln: Zuerst wird ein Template geladen, dann der Kontext über ein Dictionary erzeugt und schließlich ein HTTPResponse-Objekt zurückgegeben, das den Rückgabewert von template.render enthält.

Um den Programm-Code kompakter zu machen, bietet Django für solche Fälle eine Shortcut-Funktion render an. Mit render können wir die beiden View-Funktionen noch einmal verkürzen.

from django.shortcuts import render 

def meldungen(request):
return render(request, 'news/meldungen.html',
context={'meldungen' : Meldung.objects.all()})

def meldungen_detail(request, meldungs_id):
meldung = get_object_or_404(Meldung, id=meldungs_id)
return render(request, 'news/meldungen_detail.html',
context={'meldung' : meldung})

Der Shortcut-Funktion render wird der Pfad zu dem gewünschten Template als zweiter und der Kontext als dritter Parameter übergeben.

Wir sind nun so weit, dass wir ansprechende Ausgaben mit wenig Aufwand erzeugen können. Unser Projekt ist damit fast fertiggestellt. Es fehlt nur noch die Möglichkeit für die Besucher der Seite, Kommentare zu den Meldungen abgeben zu können.

 
Zum Seitenanfang

40.4.8    Verarbeitung von Formulardaten Zur vorigen ÜberschriftZur nächsten Überschrift

Um eine dynamische Webanwendung wirklich interaktiv werden zu lassen, müssen die Benutzer neben dem einfachen Navigieren über die Seite auch zum Inhalt der Seite beitragen können. Dies geschieht oft über Gästebücher, Foren oder Kommentarfunktionen wie in unserem Beispiel.

Auf der technischen Seite muss für diese Funktionalitäten eine Schnittstelle vorhanden sein, mit der Daten vom Browser des Benutzers an die Serveranwendung übertragen werden können.

Das HTTP-Protokoll bietet für diesen Zweck zwei Methoden für die sogenannte Argumentübertragung an: GET und POST. Der prinzipielle Unterschied zwischen den beiden Übertragungsarten ist, dass mit GET übertragene Daten direkt an die Adresse der jeweiligen Seite angehängt werden und so für den Benutzer unmittelbar sichtbar sind, während die POST-Methode für den Benutzer unsichtbar im Hintergrund Daten übertragen kann.

Beide Methoden arbeiten mit benannten Platzhaltern, die üblicherweise durch HTML-Formulare mit Daten verknüpft werden.

Unser Formular für die Kommentarabgabe

Wir werden auf der Detailseite jeder Meldung ein Formular anbieten, in dem der Besucher neue Kommentare zu der angezeigten Meldung eingeben kann. Das Formular wird zwei Textfelder besitzen: eines für den Namen des Besuchers und eines für den Kommentar selbst. Im Browser soll es später so aussehen wie in Abbildung 40.12.

Unser Kommentarformular

Abbildung 40.12    Unser Kommentarformular

Als Übertragungsart für die eingegebenen Daten wählen wir die POST-Methode, um die Adressleiste nicht zu überfüllen. Das Speichern der neuen Kommentare wird unsere View-Funktion meldungen_detail übernehmen, der beim Abschicken des Formulars ein POST-Parameter namens speichere_kommentar übergeben wird. Außerdem soll das Formular ein Feld für Fehlerausgaben besitzen, falls der Benutzer zu wenige Angaben gemacht hat.

Das Template für die Formulardefinition sieht dann folgendermaßen aus:[ 224 ](Dieser Teil wird unten in der Datei meldungen_detail.html eingefügt. Die vollständige Template-Datei finden Sie im Onlineangebot zu diesem Buch (www.rheinwerk-verlag.de/4467). )

<div class="container">
<div class="titelzeile">Neuer Kommentar</div>
<span class="fehler">{{ fehler }}</span>
<form method="post" action="">
{% csrf_token %}
<input type="hidden" name="speichere_kommentar" value="1"/>
<table>
<tr class="kommentarzeile">
<td class="spaltenbezeichner">Ihr Name:</td>
<td><input type="text" name="besuchername"
value="{{ besuchername }}"/></td>
</tr>
<tr class="kommentarzeile">
<td class="spaltenbezeichner">Kommentar:</td>
<td>
<textarea name="kommentartext">{{ kommentartext }}</textarea>
</td>
</tr>
</table>
<input type="submit" value="Abschicken" />
</form>
</div>

Wie Sie der Zeile <form method="post" action=""> entnehmen können, wird beim Versenden des Formulars die Detailseite der Meldung selbst aufgerufen. Außerdem werden die Werte der Textfelder besuchername und kommentartext als POST-Daten übergeben.[ 225 ](Das Tag {% csrf_token %} dient zum Schutz vor einem geläufigen Angriff auf Webseiten. Die genaue Bedeutung soll uns hier nicht weiter interessieren. Sie sollten in allen Formularen dieses Tag verwenden. )

Sollten beim Speichern eines Kommentars Fehler auftreten, kann eine Meldung über die Kontextvariable fehler von der View-Funktion gesetzt werden. Damit die Eingaben des Benutzers in einem solchen Fall nicht verloren gehen, können die Textfelder über die Kontextvariablen benutzername und kommentar von der View mit Initialwerten versehen werden.

Zugriff auf POST- und GET-Variablen in View-Funktionen

Wie Sie bereits wissen, bekommt jede View-Funktion von Django einen Parameter namens request übergeben. Dieser Parameter enthält Informationen über den Seitenaufruf und insbesondere die GET- und POST-Parameter. Dazu hat request zwei Attribute namens GET und POST, die den Zugriff auf die Parameter über ihre Namen wie in einem Dictionary ermöglichen.

Mit diesem Wissen können wir unsere View-Funktion model_detail folgendermaßen erweitern (die noch nicht bekannten Elemente im Listing werden anschließend erläutert):

from django.http import HttpResponseRedirect

def meldungen_detail(request, meldungs_id):
meldung = get_object_or_404(Meldung, id=meldungs_id)

if 'speichere_kommentar' in request.POST:
name = request.POST.get('besuchername', '')
text = request.POST.get('kommentartext', '')

if name and text:
kommentar = meldung.kommentar_set.create(
autor=name, text=text)
kommentar.save()
return HttpResponseRedirect('.')

else:
return render(request, 'news/meldungen_detail.html',
context={'meldung' : meldung,
'fehler': 'Geben Sie Ihren Namen und ' \
'einen Kommentar an.',
'besuchername' : name, 'kommentartext' : text})

return render(request, 'news/meldungen_detail.html',
context={'meldung' : meldung})

Am Anfang der Funktion lesen wir wie gehabt die betreffende Meldung aus der Datenbank oder geben einen Http404-Fehler zurück. Anschließend prüfen wir mit dem in-Operator, ob 'kommentar_speichern' per POST übergeben worden ist, um gegebenenfalls einen neuen Kommentar zu speichern. Wurde 'kommentar_speichern' nicht übergeben, wird der if-Block ausgelassen und die Detailseite angezeigt.

Wenn ein neuer Kommentar gespeichert werden soll, lesen wir den eingegebenen Namen und den Kommentartext aus request.POST. Anschließend prüfen wir, ob in beide Textfelder etwas eingegeben wurde. Fehlt eine Angabe, wird im else-Zweig die Detailseite erneut angezeigt, wobei ein entsprechender Fehlertext ausgegeben wird. Dadurch, dass wir die Kontextvariablen besuchername und kommentartext auf die zuvor übergebenen Werte setzen, gehen eventuell vom Benutzer gemachte Eingaben nicht verloren, sondern erscheinen wieder in den Textfeldern, wie in Abbildung 40.13 gezeigt.

Das Formular mit einer fehlerhaften Eingabe

Abbildung 40.13    Das Formular mit einer fehlerhaften Eingabe

Haben die beiden Variablen name und text korrekte Werte, erzeugen wir ein neues Kommentarobjekt in der Datenbank. Allerdings benutzen wir in diesem Fall die Klasse HttpResponseRedirect, um den Besucher zu der Detailseite weiterzuleiten, anstatt ein Template auszugeben. Der Grund dafür ist einfach: Wenn ein Besucher einen neuen Kommentar verfasst hat und nun wieder auf der Detailseite gelandet ist, könnte er die Aktualisieren-Funktion seines Browsers benutzen, um die Seite neu zu laden. Beim Aktualisieren einer Seite werden aber sowohl GET- als auch POST-Daten erneut übertragen. Deshalb würde bei jeder Aktualisierung derselbe Kommentar noch einmal gespeichert werden. Durch die indirekte Weiterleitung mittels HttpResponseRedirect lösen wir dieses Problem, da nun die POST- und GET-Variablen verworfen werden.

Sie können HttpResponseRedirect per import aus dem Modul django.http einbinden.

Damit ist unser Beispielprojekt voll funktionsfähig, sodass Meldungen aufgerufen und kommentiert werden können. Es fehlt allerdings eine Möglichkeit für den Betreiber der Seite, die Meldungen komfortabel zu verwalten. Deshalb werfen wir zum Abschluss einen Blick auf Djangos Fähigkeiten, automatisch die Administrationsoberfläche zu einer Webanwendung zu erzeugen.

 
Zum Seitenanfang

40.4.9    Djangos Administrationsoberfläche Zur vorigen ÜberschriftZur nächsten Überschrift

Eine zeitaufwendige Aufgabe bei der Erstellung einer Webanwendung ist die Entwicklung einer Administrationsoberfläche, kurz ACP (engl. Admin Control Panel). ACPs sind Werkzeuge, die den Betreibern einer Seite die Verwaltung von Seiteninhalten ermöglichen, ohne den Programm-Code verändern oder direkt auf die Datenbank zugreifen zu müssen.

Beispielsweise sollte das ACP unserer Nachrichtenseite neue Meldungen hinzufügen und alte bearbeiten oder löschen können. Ebenso sollte es möglich sein, Kommentare zu verwalten.

Im Prinzip ist ein ACP eine eigene Webanwendung, mit der sich alle Daten der zu administrierenden Anwendung bearbeiten lassen. Dementsprechend hoch ist auch der Entwicklungsaufwand.

Die gute Nachricht für Sie als angehenden Django-Entwickler ist, dass Sie sich in Ihren Projekten nur wenig um die Programmierung von ACPs kümmern müssen. Django erstellt fast vollautomatisch eine komfortable und zweckmäßige Administrationsoberfläche, sodass Sie von der lästigen Eigenimplementierung verschont bleiben. Sie müssen nur kleine Änderungen an der Konfiguration des Projekts vornehmen.

Einen Administratorbenutzer anlegen

Da die Administrationsoberfläche nicht für den öffentlichen Zugriff vorgesehen ist, legen wir zunächst einen privilegierten Benutzer an. Dazu navigieren Sie mit einer Kommandozeile in das Verzeichnis, in dem sich die manage.py Ihres Projekts befindet. In unserem Beispiel ist dies das äußere Verzeichnis news_seite. Dort rufen wir manage.py mit dem Parameter createsuperuser auf, wobei unter Windows python am Zeilenbeginn entfallen kann.

$ python manage.py createsuperuser
Username (leave blank to use 'p'): admin
Email address: waldkauz@vogel-des-jahres.org
Password:
Password (again):
Superuser created successfully.

Das Programm fragt nach dem gewünschten Benutzernamen und der E-Mail-Adresse des Administrators. Außerdem muss ein Passwort vergeben werden, mit dem sich der Administrator später im ACP anmelden kann.

Der Umgang mit Djangos ACP

Nun starten wir Djangos Entwicklungswebserver und öffnen das ACP unter http://127.0.0.1:8000/admin/, das uns mit der Login-Maske aus Abbildung 40.14 begrüßt.[ 226 ](Die Adresse des ACP wird wie alle anderen Adressen in der Datei news_seite/urls.py festgelegt. Django hat standardmäßig einen entsprechenden Eintrag eingefügt, wie Sie im Abschnitt 40.4.6 gesehen haben. )

Djangos Login zum Administrationsbereich

Abbildung 40.14    Djangos Login zum Administrationsbereich

Nach dem Einloggen zeigt sich die Standardansicht des ACP wie in Abbildung 40.15, wobei Django standardmäßig Funktionen zur Benutzerverwaltung anbietet, mit denen wir uns hier nicht weiter beschäftigen werden.

Die Startseite des Django-ACP

Abbildung 40.15    Die Startseite des Django-ACP

Damit auch unsere News-Meldungen und die Kommentare hier bearbeitet werden können, müssen wir Django entsprechend konfigurieren. Dazu registrieren wir das Modell Meldung bei der Admin-Seite, indem wir die Datei news/admin.py wie folgt anpassen:

from django.contrib import admin
from .models import Meldung

admin.site.register(Meldung)

Wenn Sie nun das ACP in Ihrem Browser neu laden, gibt es dort einen neuen Eintrag »Meldungs«[ 227 ](Django bildet den Plural immer durch Anhängen eines »s«. Dies ist manchmal nicht besonders sinnvoll, wie das Beispiel oben zeigt. ), um die Einträge unserer Meldungen zu bearbeiten (siehe Abbildung 40.16).

ACP-Eintrag zum Bearbeiten von Meldungen

Abbildung 40.16    ACP-Eintrag zum Bearbeiten von Meldungen

Jetzt können Sie jede Meldung verändern, neue Meldungen hinzufügen und Meldungen löschen. Wie Sie sehen, hat Django für die Anzeige der Meldungen die __str__-Methode des Models Meldung verwendet.

Da die Handhabung des ACP intuitiv ist, wollen wir uns als Beispiel mit dem Verändern einer Meldung begnügen. Mit einem Klick auf den Link »Meldungs« gelangen wir zur Übersicht aller Meldungen, wie in Abbildung 40.17 dargestellt.

Übersicht aller Meldungen im ACP

Abbildung 40.17    Übersicht aller Meldungen im ACP

Klicken wir beispielsweise auf die Meldung mit dem Text »Umfrage zu Django«, gelangen wir zur Editieransicht. Dort werden wir zur Demonstration den Titel in »Umfrage zum Framework Django« umändern (siehe Abbildung 40.18).

Editieren einer Meldung

Abbildung 40.18    Editieren einer Meldung

Nach dem Klick auf die Schaltfläche Sichern in der Ecke rechts unten ist die Meldung mit ihrem neuen Text in der Datenbank gespeichert, und Sie gelangen wieder zur Meldungsübersicht.

Als interessantes Feature speichert Django zu jedem Datensatz seine »Veränderungsgeschichte«, also wann wer welche Änderung an den Daten vorgenommen hat. Dieses Extra ist vor allem dann nützlich, wenn viele Anwender Zugang zur Administrationsseite haben und man den Überblick über die vorgenommenen Änderungen behalten möchte. Die Veränderungen zu einem bestimmten Datensatz können über die Schaltfläche Geschichte abgerufen werden.

Sie sollten sich vor Augen halten, welchen großen Nutzen dieser Service von Django für Sie hat: Aus einer einfachen Model-Definition werden die gesamte Datenbank und sogar das dazugehörige Verwaltungswerkzeug generiert. Diese Arbeit hätte Sie sonst Stunden gekostet.

Anpassung der Administrationsoberfläche

Das Standardlayout von Djangos ACP ist in der Praxis oft nicht optimal an die Arbeitsabläufe der Administratoren angepasst. Wenn wir beispielsweise die Kommentare zu unseren Meldungen im ACP veränderbar machen möchten, können wir wie oben eine Zeile admin.site.register(Kommentar) in die Datei news/admin.py einfügen. Dann hätten wir allerdings genau wie bei den Meldungen eine Liste aller Kommentare, ohne dass die Zugehörigkeit zur jeweiligen Meldung berücksichtigt wird. Viel natürlicher ist es, die Kommentare gemeinsam mit der zugehörigen Meldung zu bearbeiten.

Glücklicherweise bietet Django umfangreiche Möglichkeiten, um das ACP an die Wünsche des Entwicklers anzupassen. Die beschriebene Erweiterung für das Bearbeiten von Kommentaren lässt sich durch eine wie folgt angepasste Datei news/admin.py realisieren:

from django.contrib import admin
from .models import Meldung, Kommentar

class KommentarInline(admin.TabularInline):
model = Kommentar
extra = 1

class MeldungAdmin(admin.ModelAdmin):
inlines = [KommentarInline]

admin.site.register(Meldung, MeldungAdmin)

Beim Registrieren eines Modells für das ACP kann zusätzlich eine Klasse übergeben werden, die das Aussehen der ACP-Seite beeinflusst. Diese Klasse erbt dabei von der Klasse admin.ModelAdmin und ist in unserem Beispiel durch MeldungAdmin realisiert. Innerhalb dieser Klasse wird nun das Layout der ACP-Seite über Klassenattribute gesteuert. Im Beispiel nutzen wir das Attribut inlines, um die Kommentare der Meldung inline (also direkt auf der Seite) einzubinden. Der Wert für inlines ist dabei eine Liste, die alle gewünschten einzubettenden Elemente enthält. In unserem Beispiel hat sie nur den Eintrag KommentarInline. Die Klasse KommentarInline erbt von admin.TabularInline, wodurch die Einträge als Tabelle eingebettet werden. Der Wert von extra legt fest, wie viele leere Einträge zum Erstellen neuer Kommentare unter den bereits vorhandenen Kommentaren angezeigt werden sollen. Im Beispiel wird also nur ein leerer Eintrag angezeigt.

Das Ergebnis unserer Anpassung zeigt Abbildung 40.19.

Dieses Beispiel ist nur als Einblick zu verstehen. Django bietet weitere umfangreiche Anpassungsmöglichkeiten für das ACP, die den Rahmen dieses einführenden Kapitels sprengen würden.

Angepasste ACP-Seite zum Bearbeiten einer Meldung

Abbildung 40.19    Angepasste ACP-Seite zum Bearbeiten einer Meldung

Damit ist auch die Administrationsoberfläche unserer Beispielanwendung vollständig funktionsfähig. Wenn Sie Ihre Django-Kenntnisse vertiefen möchten, um auch die weiterführenden Techniken kennenzulernen, empfehlen wir Ihnen die Lektüre der ausgezeichneten Onlinedokumentation auf der Django-Homepage unter http://djangoproject.com.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Python 3 Python 3
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Einstieg in Python
Einstieg in Python


Zum Rheinwerk-Shop: Python. Der Grundkurs
Python. Der Grundkurs


Zum Rheinwerk-Shop: Algorithmen mit Python
Algorithmen mit Python


Zum Rheinwerk-Shop: Objektorientierte Programmierung
Objektorientierte Programmierung


Zum Rheinwerk-Shop: Raspberry Pi. Das umfassende Handbuch
Raspberry Pi. Das umfassende Handbuch


Zum Rheinwerk-Shop: Roboter-Autos mit dem Raspberry Pi
Roboter-Autos mit dem Raspberry Pi


Zum Rheinwerk-Shop: Neuronale Netze programmieren mit Python
Neuronale Netze programmieren mit Python


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2020
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern