www.thomas-guettler.de / Vorträge
niemand.leermann@thomas-guettler.de
Django Webframework
Dieser Vortrag ist veraltet: Die neue Version
   Einführung
1.    Wie kam ich zu Django?
1.    Überblick
1.    Darstellung: Shell / Datei
   Schritt für Schritt
2.    Projekt anlegen
2.    Anwendung (Application) anlegen
2.    Hallo Welt!
2.    Test Server
2.    Datenbank Anbindung
2.5.     Datenbank anlegen
2.5.     settings.py
2.5.     OR-Mapping
2.    DB-API
2.6.     Objekte erstellen
2.6.     Objekte verändern
2.6.     Objekte holen
2.6.     Ausblick
2.    Admin Interface
2.    Land in Sicht (Views)
   Formularbibliothek (newforms)
3.    Allgemein
3.    Beispiel
3.    Newforms Tipps
   HTTP
4.    Allgemein
4.    Middleware
4.    Request Objekt
   Unittests
   Templatesprache
   Sonstiges
   Tipps
   Best of Django Imports
10    FAQ
11    Nachteile
12    Links

1 Einführung [toc]

Vorher: 1997: CGI mit Shellscript, '98: PHP, '99: Perl, '00: Java (Cocoon, JServe/Tomcat), '01: Python+Zope, '02: Python+ZODB+Quixote.

Keine bisherige Lösung hat mir 100% gefallen.

2007: Für neues Projekt habe ich Ruby on Rails, Turbo Gears und Django verglichen und mich für Letzteres entschieden. Allgemein: Details: (*): optional Bei diesem Vortrag wird folgende Konvention eingehalten:
# Shell / Interaktiv
nutzer@rechner:~/verzeichnis$ befehl
# Datei: /pfad/zur/datei
...

2 Schritt für Schritt [toc]

Django SVN Trunk ist stabil: http://www.djangoproject.com/download/
# PYTHONPATH ggf. anpassen
tilldu@r51:~$ django/bin/django-admin.py startproject mysite
--> mysite/
    __init__.py
    manage.py
    settings.py # Konfiguration
    urls.py     # URL zu Funktion/Template Mapping
# Ein Projekt besteht aus N Anwendungen
# Projekt ('mysite') Anwendungen: Admin-Interface, Quiz, Blog, Wiki, ...
tilldu@r51:~$ cd mysite
tilldu@r51:~/mysite$ python manage.py startapp quiz
--> quiz/
    __init__.py
    models.py
    views.py
Django lässt sich auch ohne Datenbank, ohne Admin-Interface und ohne Templates verwenden:
#Datei mysite/urls.py: (URLs des gesamten Projekts)
    ...
    '^quiz/', include('quiz.urls') # Ähnlich dem Unix mount-Befehl
    ...
#Datei mysite/quiz/urls.py: (URLs dieser Applikation)
from django.conf.urls.defaults import patterns

urlpatterns = patterns('',
                       # URL http://.../quiz/ 
                       # quiz=Verzeichnis views=Datei hallo=Funktion 
                       (r'^$', 'quiz.views.hallo') 
                       )
#Datei mysite/quiz/views.py
import django
def hallo(request):
    return django.http.HttpResponse("<html><body>Hallo Welt!</body></html>")
tilldu@r51:~/mysite$ manage.py runserver (Testserver, http://localhost:8000/)
tilldu@r51:~/mysite$ firefox http://localhost:8000/quiz/
# Postgres (SQLite, MySQL oder Oracle)
# Wenn Postgresnutzer dem Unixnutzernamen entspricht,
# ist kein Passwort nötig.
postgres@r51:~$ createuser tilldu
Soll die neue Rolle ein Superuser sein? (j/n) n
Soll die neue Rolle Datenbanken erzeugen dürfen? (j/n) j
Soll die neue Rolle weitere neue Rollen erzeugen dürfen? (j/n) n
tilldu@r51:~$ createdb -E unicode mysite
CREATE DATABASE
tilldu@r51:~/mysite$ $EDITOR settings.py  # Projektweite Einstellungen
--> Datenbankverbindung konfigurieren Beim Objektrelationalem Mapping (Siehe auch: Wikipedia OR-Mapping) werden die Objekte des Programms in einer relationalen Datenbank gespeichert.
# mysite/quiz/models.py
from django.db import models

class Question(models.Model):
    text=models.CharField(max_length=1024)
    answer=models.CharField(max_length=64)
    explanation=models.CharField(max_length=1024, blank=True)
    class Admin:
        pass

    def __unicode__(self):
        return u'#%s %s' % (self.id, self.text)
Klasse --> Tabelle
Attribut --> Spalte
1:N Beziehung --> ForeignKey(OtherClass)
N:M Beziehung --> ManyToManyField(OtherClass)
Klassen für Attribute/Spalten: AutoField, BooleanField, CharField, DateField, DecimalField, FileField, IntegerField ...

Siehe Django Doku: Creating Models
Tabellen erzeugen:
tilldu@r51:~/mysite$ python manage.py syncdb
Mit der SQL-Shell die angelegten Tabellen anzeigen:
psql (Postgres Datenbankshell)
 \d --> Tabellen anzeigen (appname_klassenname)
 \d quiz_question (Anzeigen der Spalten)
tilldu@r51:~/mysite$ python manage shell
from quiz.models import Question
question=Question(text="...", answer="...")
question.save() # --> INSERT 
question.explanation="..."
question.save() # --> UPDATE

# Auf eine bestimmte Frage zugreifen
Question.objects.get(id="...") # Zugriff über die *Klasse*

# Alle Fragen
Question.objects.all()

# SELECT ... WHERE text ILIKE '%foo%'
# Siehe Django Doku: Field Lookups
Question.objects.filter(text__icontains="foo") 

# Anzahl der Fragen: SELECT COUNT(*) 
Question.objects.filter(...).count() 
SQL JOIN
# Datei mysite/quiz/models.py:

class Group(models.Model):
    name=models.CharField(max_length=32)

class Question(models.Model):
    ...
    group=ForeignKey(Group, related_name="questions") # 1:N Beziehung
# API Aufruf
Question.objects.filter(group__name="Geographie") # SELECT ... JOIN 

# entspricht
Groups.objects.get(group="Geographie").questions
# Datei mysite/settings.py:
 INSTALLED_APPS = ... 'django.contrib.admin', ...
tilldu@r51:~/mysite$ python manage.py syncdb (Tabellen werden erzeugt)
tilldu@r51:~/mysite$ $EDITOR urls.py (Admin URLs einbinden)
# Datei mysite/quiz/urls.py: 
    ...
   # URL http://.../quiz/ask/2/
   (r'^ask/(?P<question_id>\d+)/$', 'quiz.views.ask'),
   (r'^random/$', 'quiz.views.rand')
    ...
# Datei mysite/quiz/views.py:

def ask(request, question_id):
    question=Question.objects.get(id=question_id)
    if request.method=="POST":
        # Jemand versucht die Frage zu lösen.
        post=request.POST
    else:
        # Das Formular wird zum ersten Mal aufgerufen.
        post=None
    form=QuestionForm(question, post)
    ...

def rand(request):
    count=Question.objects.all().count() # SELECT count(*) FROM quiz_question
    i=random.randint(0, count-1)
    question=Question.objects.all()[i]   # SELECT cols... FROM ... LIMIT 1 OFFSET N

    # Anzeigen der bisherigen SQL-Anweisungen
    #assert False, django.db.connection.queries 

    return django.http.HttpResponseRedirect("/quiz/ask/%d/" % question.id)


3 Formularbibliothek (newforms) [toc]


Siehe Django Doku: Newforms
class QuestionForm(forms.ModelForm):
    class Meta:
        model=Question
    def clean_answer(self):
        answer=self.cleaned_data["answer"]
        if self.instance.answer!=answer:
            raise forms.ValidationError("Die Antwort ist falsch. Richtig: %s" %
                                         self.question.answer)

4 HTTP [toc]

Django wird in der Regel über mod_wsgi, SCGI oder FastCGI angebunden. Bei diesen Methoden wird ein Server gestartet, der den Python-Interpreter lädt und diesen Interpreter für die nächsten Requests wieder verwendet.

Die Anbindung mittels einfachem CGI (für jeden HTTP-Request wird ein Python Interpreter gestartet) ist möglich, aber nicht zu empfehlen. Siehe Django Wiki: ServerArrangements

Mein Tipp:
  1. mod_wsgi
Eine Middleware stellt Methoden bereit, die immer am Anfang und am Ende jedes Requests aufgerufen werden.

Beispiele: Methoden, die bereitgestellt werden können:
 process_request(self, request)
 process_response(self, request, response)
 process_exception(self, request, exception)
Beispiele: Siehe Djano Doku: Middleware Attribute des Request Objekts:
POSTPOST Parameter (<form action="./" method="POST">)
GETGET Parameter http://.../mypage/?variable=wert
METADictionary HTTP_REFERER=http://..
pathURL des Aufrufs (ohne Server und ohne GET Parameter)
Hinweis: POST und GET sind zwei verschiedene Dictionaries. POST sollte verwendet werden, wenn mit dem Request Daten auf dem Server verändert werden. GET sollte für lesende Zugriffe verwendet werden.
Siehe Django Doku: Request+Response

5 Unittests [toc]

Vertrauen ist gut, Kontrolle ist besser
Django bietet zwei Möglichkeiten des automatisierten Testens: Für die Tests wird temporär eine neue Datenbank angelegt. Optional können Testdaten in die Datenbank geladen werden. Nach dem Test wird die Testdatenbank wieder gelöscht.

Sehr praktisch ist der Test Client. Damit kann man die Aufrufe mittels einem Webbrowser simulieren:
# Datei mysite/quiz/tests.py
from django.test import TestCase

class SimpleTest(TestCase):
    def test_add(self):
        response = self.client.post('/quiz/question/add/', 
            {'text': 'Hauptstadt von Frankreich', 'answer': 'Paris'})
        self.assertContains(response, 'wurde angelegt', status_code=200)
Unittest ausführen:
tilldu@r51:~/mysite$ manage.py test                          # Alle Tests aller Anwendungen 
tilldu@r51:~/mysite$ manage.py test quiz                     # Alle Tests der Anwendung 'quiz'
tilldu@r51:~/mysite$ manage.py test quiz.SimpleTest.test_add # Einen einzelnen Test aufrufen

Siehe: Django Doku: Testing

6 Templatesprache [toc]

Die Templatesprache von Django ist sehr vielseitig. Sie unterstützt z.B. Vererbung: In abgeleiteten Templates können einzelne Blöcke des Elterntemplates überschrieben werden.

Siehe Django Doku: Templates for Authors und Templates for Programmers.

Besonders interessant ist Autoescape.

7 Sonstiges [toc]

8 Tipps [toc]

9 Best of Django Imports [toc]

Folgende Import Anweisungen führe ich oft aus:
from django.newforms import forms
from django.conf import settings # Settings-Datei des Projekts
from django.core.urlresolvers import reverse
from django.utils.html import conditional_escape as escape
from django.utils.safestring import mark_safe
from django.http import HttpResponse, HttpResponseRedirect
from django.utils.http import urlquote

10 FAQ [toc]

11 Nachteile [toc]

12 Links [toc]


© 2007 Thomas Güttler. Der Text darf nach belieben kopiert und modifiziert werden, solange dieser Hinweis zum Copyright und ein Links zu dem Original unter www.thomas-guettler.de erhalten bleibt. Es wäre nett, wenn Sie mir Verbesserungsvorschläge mitteilen: guettli@thomas-guettler.de
Dieser Vortrag ist veraltet: Die neue Version