.. _first_app: .. index:: single: App single: CRUD single: App-Name single: URLs entkoppeln single: Model-View-Template single: Pattern Matching single: Request single: Request-Objekt 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. .. admonition:: 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. .. code-block:: shell (eventenv) python manage.py startapp events manage.py hat uns nun ein weiteres Verzeichnis ``events`` erstellt: .. code-block:: shell 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 .. admonition:: 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. ``_ 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: .. code-block:: shell 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. .. admonition:: 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. .. image:: /images/mvt.png 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: .. code-block:: shell └── events | migrations │ admin.py │ apps.py │ models.py │ views.py | urls.py <= diese Datei wurde manuell hinzugefügt │ __init__.py .. admonition:: 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. .. code-block:: python from django.http import HttpResponse def hello_world(request) -> HttpResponse: return HttpResponse("Djangoheroes to the rescue!") .. admonition:: 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: .. code-block:: python from django.http import HttpResponse def hello_world(request) -> HttpResponse: raise ValueError("Exception is coming home!") return HttpResponse("Djangoheroes to the rescue!") .. admonition:: 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: .. code-block:: python 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: ``_ 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: .. code-block:: python 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 App ``events`` inkludiert und die Prüfung für den restlichen Teil des URL-Pfades wird in den ``urls.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. .. admonition:: 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: .. code-block:: python 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 ``_ in den Browser eingeben, sollten wir eine weiße Webseite mit dem Text "Djangoheroes to the rescue!" auf dem Bildschirm sehen .. code-block:: shell (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: .. code-block:: python 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, ``_, 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: ``_ .. admonition:: 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.