Das Anlegen der ersten App
Wir wollen nun unsere erste App
anlegen. Diese App soll sich um das Verwalten
der Events in unserem Projekt kümmern und heisst deshalb auch events
.
Verwalten heisst zum Beispiel: Anlegen neuer Events, Löschen und Auflisten von
bestehenden Events, Anzeigen von Events auf einer Detailseite.
Insbesondere müssen also die grundlegenden CRUD-Operationen implementiert werden, die im Einsatz von Datenbanken bzw. sonstigem persistenten Speicher nötig sind.
CRUD
Das Akronym CRUD steht für:
Create: Anlegen eines Datensatzes
Retrieve: Lesen eines Datensatzes
Update: Datensatz aktualisieren
Delete: Datensatz löschen
App events anlegen
Um eine App innerhalb eines Projekts anzulegen, nutzen wir das Sub-Kommando startapp
gefolgt von dem Namen der gewünschten App.
(eventenv) python manage.py startapp events
manage.py hat uns nun ein weiteres Verzeichnis events
erstellt:
event_manager
├── manage.py
├── db.sqlite3
├── events
├── migrations/
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
├── views.py
└── event_manager
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
Was ist ein guter App-Name?
Bei Django-Apps handelt es sich um Python Packages, deshalb benötigen sie auch einen validen Namen nach der Python-Konvention. https://www.python.org/dev/peps/pep-0008/
Der Name einer App sollte also kurz sein und nur Kleinbuchstaben verwenden. Unterstriche können verwendet werden, wenn dies die Lesbarkeit erhöht. Der App Name kann singular oder plural sein. Es muss sich um einen validen Python Package Namen handeln.
Beispiele für gute App Namen
geodjango
pandas
blog
Beispiele für schlechte App Namen
python_django_blog_app
BLOGGER
Das Verzeichnis der neuen App
Wenn wir das Verzeichnis events
jetzt mal genauer betrachten, sehen wir darin folgendes:
event_manager
└── events
| migrations
│ admin.py
│ apps.py
│ models.py
│ tests.py
│ views.py
│ __init__.py
__init__.py
Die Datei __init__.py
definiert, wie schon beim Konfigurationsverzeichnis, dass das Verzeichnis events
ein Python Paket ist.
models.py
Die Models unserer Applikation, also die Geschäftsobjekte, werden wir gleich selber in der Datei models.py
anlegen. Daraus werden dann später u.a. die Datenbank-Tabellen generiert.
tests.py
Unit-Tests werden per default in der Datei tests.py
erstellt. Diese Datei werden wir jetzt löschen und später hier ein Test-Verzeichnis erstellen.
views.py
Last but not least die wohl wichtigste Datei: In der views.py
liegen die Views der Applikation. Die Views sind die Steuer-Funtionen unserer App und reagieren auf die diversen Aktionen mit einer HTTP-Response.
Model-View-Template Muster
Django verwendet eine Unterart des Model-View-Controller
Musters, nämlich das MVT-Musters.
Der klassische View ist bei Django ein Template, wohingegen der View in Django in etwa dem Controller entspricht. Die Models sind die Models und repräsentieren die Geschäftsobjekte.
urls.py
Was jetzt noch fehlt, ist eine Datei urls.py
, damit wir die Views auch ansteuern können.
Das holen wir jetzt nach und erstellen unter event_manager/events
die Datei urls.py
. Das Verzeichnis events
sollte nun so aussehen:
└── events
| migrations
│ admin.py
│ apps.py
│ models.py
│ views.py
| urls.py <= diese Datei wurde manuell hinzugefügt
│ __init__.py
URLs entkoppeln
Unter event_manager/event_manager/urls.py
liegen die Projekt-URLs. Rein prinzipiell könnte man auch alle URLs, die im Projekt verwendet werden, dort notieren. Das hätte aber den Nachteil, dass die URLs der App mit dem Projekt und anderen Apps komplett verdrahtet sind.
Unser Ziel ist es aber, jede App so unabhängig wie möglich zu machen und eine loose Koppelung der Apps mit dem Projekt zu erreichen. Wir notieren also alle URLs, die die App betreffen, auch in der App und importieren diese dann später in die Projekt-URLs. Somit wäre eine loose Koppelung erreicht.
Deshalb gilt es als good Practice, jeder App eine eigene urls.py
zu geben.
Die erste View
Die erste View in unserer App soll nur eine Test-View sein, damit wir sehen, ob unsere App funktioniert. Dazu modifizieren wir die Datei event_manager/events/views.py
. Eine View muss IMMER einen Http-Response zurückgeben, deshalb importieren wir aus django.http
die HttpResponse
.
HttpResponse
ist eine Klasse aus dem http
-Paket von Django. Sie erstellt uns den nötigen HTTP-Response inklusive dem Header, den wir dann aus der View an den Client zurückgeben können.
from django.http import HttpResponse
def hello_world(request) -> HttpResponse:
return HttpResponse("Djangoheroes to the rescue!")
Aufgabe einer View
Jede View ist für eine von zwei Aufgaben verantwortlich: ein HttpResponse-Objekt zurückzugeben, welches den Inhalt für die angeforderte Aktion enthält oder dem Auslösen einer Exception.
Die View kann Datensätze aus einer Datenbank lesen, Daten in ein Template rendern, eine PDF-Datei generieren, eine XML-Datei oder zip-Datei erstellen. Was auch immer in der View passiert, sie muss ein HttpResponse-Objekt zurückzugeben. Ausnahme ist lediglich der Fehlerfall, wenn eine Exception erhoben wird.
Diese View löst zum Beispiel eine Exception aus:
from django.http import HttpResponse
def hello_world(request) -> HttpResponse:
raise ValueError("Exception is coming home!")
return HttpResponse("Djangoheroes to the rescue!")
Moment, was ist denn eine View überhaupt?
Im Django-Framework sind Views Python-Funktionen oder -Klassen, die eine HTTP-Anfrage entgegennehmen (Request) und eine Http-Antwort (Response) zurückgeben. Ein User, der eine Anfrage an unser Webprojekt stellt, wird also über den URL-Resolver auf eine View geleitet.
Views werden grundsätzlich in der Datei views.py
einer App erstellt. Da eine App viele Views
haben kann, kann die views.py
ziemlich groß und unübersichtlich werden.
Deshalb sollte man sich bemühren, hier Ordnung zu halten.
App in den Settings registrieren
Damit die App von Django später bei der Datenbank-Migration auch gefunden wird, müssen wir sie in den Settings registrieren. Dazu öffnen wir die Datei event_manager/event_manager/settings.py
und modifizieren folgende Zeilen:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"events", # <= diesen Eintrag hinzufügen
]
Mehr zu den Settings gibt es in der Doku: https://docs.djangoproject.com/en/stable/settings/
URLs festlegen
Um die View jetzt aufrufen zu können, müssen wir für dieses App die URL anlegen, d.h. der Pfad, den der User im Browser eingeben muss, um diese App aufrufen zu können.
Projekt URLs
Dazu öffnen wir erstmal die Projekt-URLs unter event_manager/event_manager/urls.py
und modifzieren den Inhalt wie folgt:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("events/", include("events.urls")),
]
Pattern Matching
Hier haben wir jetzt den ersten Teil der URL festgelegt. Django untersucht die
von uns im Browser eingegebene URL und prüft, ob der erste Teil des URL-Pfades
davon mit dem ersten Argument von
path
matcht.
http://127.0.0.1/super/hello_heroes super matcht weder mit dem Begriff admin noch mit events. Es wird kein URL-Pfad gefunden, und Django serviert uns einen 404-Fehler. Prüfen wir das doch gleich mal.
http://127.0.0.1/events/hello_pingus events matcht mit events. Also werden nun die
urls.py
aus der Appevents
inkludiert und die Prüfung für den restlichen Teil des URL-Pfades wird in denurls.py
der inkludierten App fortgeführt.
Include
Der include-Befehl
importiert alle Url-Angaben, die in event_manager/events/urls.py
festgelegt wurden.
Mit dem namespace
-Keyword-Argument behalten wir uns die Möglichkeit vor, die URLs der App unter einem
anderen Namespace aufzurufen. In diesem Buch beschäftigen wir uns mit dieser Möglichkeit nicht weiter, es ist
aber gute Practice, das hier gleich mit anzugeben. Der Namespace sollte hier per default einfach der Name der App sein.
Der Pfad zu unserer App
Unsere App heisst events
und so haben wir uns entschlossen, jede
erdenkliche URL zu dieser APP, die wir später entwerfen werden, ebenfalls
mit events anzusprechen, nämlich im ersten Argument der path
-Funktion.
Das könnte zum Beispiel die URL http://127.0.0.1:8000/events/event/3
für
eine Detail-Ansicht eines Events sein oder http://127.0.0.1:8000/events
für die Übersicht aller Events.
Wichtig: Dieser Pfad-Name muss nicht so heissen, wie die App! Wir hätten auch definieren können, dass der Pfad ereignisse
heisst.
http://127.0.0.1:8000/ereignisse/3
Wir sind also im URL-Design relativ flexibel und können das jederzeit im Projektverlauf anpassen!
App URLs
Jetzt müssen noch die URLs auf App-Ebene festgelegt werden. Dazu öffnen wir die Datei event_manager/events/urls.py
und modifzieren den Inhalt wie folgt:
from django.urls import path
from . import views
app_name = "events"
urlpatterns = [
path("hello-world", views.hello_world, name="hello_world"),
]
in der Variable app_name
wird immer der Name der jeweiligen App eingetragen. Anhand dieser Variable können wir später im Template auf die URLs referenzieren.
Auch hier haben wir wieder eine urlpatterns-Liste. Diese wird später in event_manager/event_manager/urls.py
per include
inkludiert. Das name
-Argument von path()
sollte eindeutig sein. Keine schlechte Idee ist es, es genauso zu bennen, wie die View in dem jeweiligen Path.
Gucken wir uns nochmal die URL an, die der User eingeben wird:
http://127.0.0.1:8000/events/hello-world
Damit hätten wir auch den zweiten Teil der URL, also das hello-world
definiert. Wenn wir jetzt nochmal den Runserver starten und http://127.0.0.1:8000/events/hello-world in den Browser eingeben, sollten wir eine weiße Webseite mit dem Text „Djangoheroes to the rescue!“ auf dem Bildschirm sehen
(eventenv) python manage.py runserver
Das Request-Objekt
Jede View, sei sie funktions- oder klassenbasiert, erhält das Request-Objekt
als Argument übergeben. Das Request-Objekt enthält alle möglichen Daten über
den HTTP-Request wie die GET-und POST Daten, URL-Pfade oder gesendete
Cookies. Mit request.method
kann zum Beispiel die gewählte HTTP-Methode
untersucht werden.
Mit print(dir(request))
können wir das Request-Objekt auch untersuchen.
Verändern wir die hello_world Funktion nochmal und lassen uns ein paar Informationen ausgeben:
from django.http import HttpResponse
def hello_world(request) -> HttpResponse:
print("das Request Objekt: ", dir(request))
print("verwendete HTTP-Methode: ", request.method)
print("Der User: ", request.user)
return HttpResponse("Djangoheroes to the rescue!")
Wenn wir jetzt nochmal die URL im Browser aufrufen, http://127.0.0.1:8000/events/hello-world, können wir in der Ausgabe der Shell den angemeldeten User und die verwendete HTTP-Methode sehen.
Mehr zum Request-Objekt findet sich in der Doku: https://docs.djangoproject.com/en/stable/request-response/
Print-Aufrufe in der View
Standardmäßig werden print-Aufrufe in einer View auf der Konsole
ausgegeben. Das ist für die ersten Schritte in Ordnung. Später werden wir
noch eine bessere Methode kennenlernen, nämlich das Loggen mit dem
logging-Modul
in Dateien.
Mehr zum Anlegen von Apps findet ihr im offiziellen Django-Tutorial:
Zusammenfassung
Eine Zusammenfassung aller nötigen Schritte zum Erstellen eines Projekts und Anlegen einer ersten App findet ihr im Kapitel Cookbooks: Cookbook 1, ein Projekt erstellen.