5. Django

Grundlagen zu Django
URL-Mapping
Das MVC-Konzept in Django
Die Model-Komponente
Die View-Komponente
Die Templates

5.1 Grundlagen zu Django

Django wird seit 2005 im Rahmen eines Projektes bei World Online entwickelt und ist speziell auf schnell wechselnden, und große Mengen an Content ausgelegt. Das gesamte Framework steht, sammt Quellcode, kostenlos zur Verfügung und darf beliebig verwendet werden. Benannt wurde es nach dem Jazz-Gitarristen Django Reinhard.

Bei Django handelt es sich um ein Full-Stack-Web-Framework, welches mit einem beliebigen Server betrieben werden kann, welcher CGI, WSGI oder mod_python unterstützt. Neben den Standard-Anforderungen an ein Web-Framework sind besonders das einfache Session-Handling, die Möglichkeit zur Internationalisierung und der Skalierbarkeit zu erwähnen.

Um dem Entwickler ein schnelles Vorranschreiten in einem Projekt zu ermöglichen, realisiert Django einige Prinzipien, auf die im folgenden kurz eingegangen wird. Diese sollen aber nicht nur dem Benutzer zur Hand gegeben werden, es wird zusätlich gefordert dass dieser sich möglichst bei der Entwicklung der eigenen Web-Anwendungen unter Django an diese hält:

Eines der grundlegendsten Eigenschaften von Django ist die geringe Kopplung zwischen den einzelnen Komponenten. Dies ermöglicht einen einfachen Austausch und eine einfache Erweiterungsmöglichkeit um neue Elemente. Es bietet sich an, die eigenen Anwendungen eben so entkoppelt zu gestalten, um die Wiederverwendung zu vereinfachen.

Ein weiteres Prinzip ist das DRY-Konzept (Don't Repeat Yourself). Dieses fordert Codeverdoppelungen zu umgehen und doppelte Vorkommen sinnvoll in einer Methode oder einem Modul zu kapseln. So ist gewährleistet, dass Änderungen an genau einer Stelle durchgeführt werden brauchen und alle abhängigen Komponenten automatisch angepasst werden.

Eine weitere Eigenschaft von Django ist, das explizite Angaben gegenüber impliziten Angaben vorgezogen werden. Dies ist bereits ein in Python realisiertes Konzept und verhindert für den Benutzer möglicherweise unerwartetes Verhalten (Magic). Beispielsweise weisen Namen von Funktionen nur auf die Aufgabe hin, der Name selbst aber bringt keine zusätzliche Funktionalität.

5.2 URL-Mapping

Django hat eine ganz eigene Art um Adressen auf Objekte (Seiten) zu mappen. Für diese Aufgabe wird eine extra URL-Konfigurations-Datei angelegt, welche diesen Vorgang beschreibt. Diese Datei enthält eine Reihe an Regulären Ausdrücken, denen Methoden aus Objekten zugeordnet werden. Passt eine Anfrage eines Clienten auf einen Ausdruck, so wird die entsprechende Funktion aufgerufen und das HTML erzeugt. Trifft keines der Muster auf die gegebene Adresse zu, so wird eine Fehlerseite generiert. Eine Beispiel für die Konfigurations-Datei und eine Anfrage an Django ist im folgenden Code-Block gegeben:

from
django.conf.urls.defaults
import
*

urlpatterns = patterns(
"",
(r
"/articles/(\d{4})/$"
,
"mysite.views.year_archive"
),
(r
"/articles/(\d{4})/(\d{2})/$"
,
"mysite.views.month_archive"
),
(r
"/articles/(\d{4})/(\d{2})/(\d+)/$"
,
"mysite.views.article_detail"
)
)

#Anfrage: www.spam.de/articles/2005/05/42/

mysite.views.article_detail(request,
"2005"
,
"05"
,
"42"
)
In diesem Beispiel werden drei verschiedene Reguläre Ausdrücke definiert, denen verschiedene Methoden zugeordnet werden. Es wird die Methode aufgerufen, dessen Ausdruck als erster dem angeforderten Dokument entspricht. In der unteren Hälfte ist eine konkrete Anforderung gegeben und wie diese nach dem obigen Schema ausgeführt werden würde. Alle erkannten Attriute werden nach Erkennungs-Reihenfolge als Parameter an die aufzurufende Methode weiter gereicht.

Der request-Parameter enthält noch weitere Informationen über die Anfrage und wird immer als erster Parameter übergeben. Hinterlegt sind dort beispielsweise Informationen über den aktuellen Session-Zustand der Anfrage oder auch Cookie-Belegungen.

5.3 Das MVC-Konzept in Django

Wie in vielen anderen Web-Frameworks auch, realisiert Django das MVC-Konzept zur Teilung von Logik, Inhalt und Layout. Es ist jedoch nur eine sehr lockere Implementierung vorhanden, genau in dem Umfang dass die eigentliche Entwicklung nicht durch besondere Einschränkungen behindert wird, aber so streng, dass der Programmierer nicht vollkommen vom MVC-Prinzipt abweichen kann. Hinzu kommt die Umbenennung der typischen MVC-Komponenten, welches in der folgenden Tabelle dargestellt ist. Auf der linken Seite stehen die Bezeichnungen wie sie allgemein, wie beispielsweise in Ruby on Rails, verwendet werden, auf der rechten die in Django äquivalenten Namen:

Ruby on RailsDjango
ModelModel
ViewTemplates
ControlerView
Das Modell heißt wie allgemein üblich auch in Django weiter in Model. Die Views wurden umbenannt in Templates, da diese nicht mehr als Vorlagen sind, und deren Aufgabe so am eindeutigsten beschrieben ist. Der Controller heißt in Django View, da dieser die Schnittstelle zwischen Client und Django realisiert, also der einzige, für den Benutzer von außen sichtbare Zugriffspunkt ist. Die lose Kopplung von View, Template und Model sind in Schaubild 5.3.1 dargestellt.


Abbildung 5.3.1.: Aufbau von Django
Wie man sehen kann, ist die Aufgabe des Views die Anfragen von außen entgegen zu nehmen und entsprechende Antworten zu generieren. Dazu wird im View zunächst ein "HTTPRequest"-Objekt erzeugt, welches Informationen über die aktuelle Session und auch Cookies enthält. Dies ist genau der "request"-Parameter, welcher in 5.2 schon angesprochen wurde. Im nächsten Schritt wird das URL-Mapping durchgeführt und ein entsprechender Handler des Views aufgerufen. Dieser startet nun gegebenenfalls Anfragen an die Datenbank und speist mit diesen die Templates. Abschließend wird die gerenderte Seite dann zurück an den Client, über den Web-Server, geschickt.

5.4 Die Model-Komponente

Der Aufbau von Modellen in Django ist sehr ähnlich zu dem Prinzip von TurboGears, daher wird dieser Abschnitt nur sehr kurz gehalten. Tabellen werden ebenfalls als Klassen definiert, Attribute durch deren Angabe und das Anlegen eine Instanz einer Typ-Klasse um den Typ einer Spalte zu definieren. Dazu stehen nahezu die gleichen Methoden und Objekte zur Verfügung, die sich lediglich in den Namen und einigen Feinheiten unterscheiden. Der wichtigste Unterschied zwischen Django-Modellen und TurboGears-Modellen ist, dass Änderungen die in TurboGears an einem Datensatz durchgeführt werden sich sofort in der dahinter stehenden Tabelle auswirken, in Django muss die Änderung explizit durch ein "save"-Statement durchgeführt werden.

5.5 Die View-Komponente

Wie oben bereits beschrieben, enthalten die Views die gesamte Funktionalität einer Anwendung. Dazu werden die Modelle und Templates verknüpft und aus ihnen entsprechende HTML-Seiten generiert. Das Prinzip läßt sich in dem folgenden Code-Abschnitt gut erkennen:

#Variange 1: Der umständliche Weg


from
django.template
import
Context, loader
from
mysite.polls.models
import
Poll
from
django.http
import
HttpResponse

def
index
(self):
#Anfrage an Model

latest_poll_list = Poll.objects.all().order_by(
"-pub_date"
)[:5]

#Template laden

t = loader.get_template(
"polls/index.html"
)

#Kontext aus Modell-Daten vorbereiten (als Dictionary)

c = Context({
"latest_poll_list"
:latest_poll_list})

#Ergebnis zurücksenden

return
HttpResponse(t.render(c))
Da im Normalfall immer ein Template und ein Kontext benötigt werden, kann das obrigen Beispiel auch verküzt geschrieben werden:
#Variange 2: Der schnelle Weg


from
django.shortcuts
import
render_to_response
from
mysite.polls.models
import
Poll

def
index
(self):
#Anfrage an Model

latest_poll_list = Poll.objects.all().order_by(
"-pub_date"
)[:5]

#Ergebnis zurücksenden

return
render_to_response(
"polls/index.html"
, {
"latest_poll_list"
:latest_poll_list})
In beiden Fällen wird das Template aus "polls/index.html" geladen.

5.6 Die Templates

Prinzipiell sind die Templates in Django aufgebaut wie viele andere auch, jedoch gibt es einige interessante Ansätze die im folgenden Beispiel verdeutlicht werden sollen:

{% extends "base.html" %}

{% block title %}Articles for {{ year }}{% endblock %}

{% block content %}
<h1>Articles for {{ year }}</h1>

{% for article in article_list %}
<p>{{ article.headline }}</p>
<p>By {{ article.reporter.full_name }}</p>
<p>Published {{ article.pub_date|date:"F j, Y" }}</p>
{% endfor %}
{% endblock %}
Die erste Zeile gibt an, dass dieses Template von dem Template "base.html" abgeleitet ist. So wird es ermöglicht, Seiten einen grundlegenden Aufbau zu übergeben und nur die speziellen Elemente anzupassen. Änderungen müssen dann im Normalfall nur noch an einer einzigen Stelle durchgeführt werden, wirken sich aber auf alle abgeleiteten Templates aus. Die Basis-Klasse dieses Templates ist im nächsten Code-Abschnitt dargestellt.

Weiter gibt es noch Elemente, mit denen die sturkturelle Logik des Templates programmiert werden kann. Diese Elemente beginnen mit {% und enden mit %}. Dazwischen steht dann ein konkreter Befehl, oder aber auch der Abschluss eines Blocks. Um eine Vermischung von Anwendungslogik und Aussehen zu vermeiden, handelt es sich bei den Befehlen um eine eigene Template-Sprache. Diese unterstütz nur einige Anweisungen wie "block;", "if" und "for", jedoch keine beliebigen Python-Anweisungen.

Möchte man Werte von Variablen einbringen so sind diese in doppelten geschwungenen Klammern anzugeben: {{ Variable }}. Wenn diese im übergebenen Context vorhanden ist, wird das gesamte Tag durch den Wert ersetzt.

In der letzten Zeile des for-Blocks ist noch ein weiteres nützliches Feature der Template-Sprache dargestellt: das Pipelining. Das Verhalten ist gleich zu dem der üblichen Unix-Systeme. In diesem konkreten Beispiel wird ein Datum formatiert.

<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<img src="sitelogo.gif" alt="Logo" />
{% block content %}{% endblock %}
</body>
</html>
Das obige Beispiel ist die Basis-Klasse zum vorangegangenen Beispiel. Wie deutlich zu erkennen ist, wurde der grundlegende Aufbau der Seite festgelegt. Auch hier können wieder Blöcke definiert werden, die bei einem Aufruf eines Kind-Templates, durch den dort entsprechenden ersetzt werden, falls dieser vorhanden ist. An sonsten wird der Inhalt des Eltern-Blocks verwendet.