Das Erstellen von öffentlichen Schnittstellenansichten

DjangoDjangoBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

Dieser Tutorial beginnt dort, wo Set Up the Database aufgehört hat. Wir setzen die Web-Umfrageanwendung fort und werden uns auf die Erstellung der öffentlichen Schnittstelle - den "Views" - konzentrieren.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL django(("Django")) -.-> django/CoreConfigurationandRoutingGroup(["Core Configuration and Routing"]) django(("Django")) -.-> django/DatabaseModelsandMigrationsGroup(["Database, Models, and Migrations"]) django(("Django")) -.-> django/UserInterfaceandInteractionGroup(["User Interface and Interaction"]) django/CoreConfigurationandRoutingGroup -.-> django/django_urls("Django Urls") django/CoreConfigurationandRoutingGroup -.-> django/django_exceptions("Django Exceptions") django/DatabaseModelsandMigrationsGroup -.-> django/models("Models") django/DatabaseModelsandMigrationsGroup -.-> django/databases("Databases") django/DatabaseModelsandMigrationsGroup -.-> django/request_and_response("Request and Response") django/DatabaseModelsandMigrationsGroup -.-> django/schemaeditor("SchemaEditor") django/UserInterfaceandInteractionGroup -.-> django/templates("Templates") subgraph Lab Skills django/django_urls -.-> lab-153743{{"Das Erstellen von öffentlichen Schnittstellenansichten"}} django/django_exceptions -.-> lab-153743{{"Das Erstellen von öffentlichen Schnittstellenansichten"}} django/models -.-> lab-153743{{"Das Erstellen von öffentlichen Schnittstellenansichten"}} django/databases -.-> lab-153743{{"Das Erstellen von öffentlichen Schnittstellenansichten"}} django/request_and_response -.-> lab-153743{{"Das Erstellen von öffentlichen Schnittstellenansichten"}} django/schemaeditor -.-> lab-153743{{"Das Erstellen von öffentlichen Schnittstellenansichten"}} django/templates -.-> lab-153743{{"Das Erstellen von öffentlichen Schnittstellenansichten"}} end

Überblick

Eine Ansicht ist eine "Art" von Webseite in Ihrer Django-Anwendung, die im Allgemeinen eine bestimmte Funktion erfüllt und eine bestimmte Vorlage hat. Beispielsweise könnte eine Blog-Anwendung die folgenden Ansichten haben:

  • Blog-Hauptseite - zeigt die neuesten Einträge an.
  • Eintrags-"Detail"-Seite - Permalink-Seite für einen einzelnen Eintrag.
  • Jahresarchivseite - zeigt alle Monate mit Einträgen im angegebenen Jahr an.
  • Monatsarchivseite - zeigt alle Tage mit Einträgen im angegebenen Monat an.
  • Tagesarchivseite - zeigt alle Einträge am angegebenen Tag an.
  • Kommentaraktion - behandelt das Absenden von Kommentaren zu einem angegebenen Eintrag.

In unserer Umfrageanwendung werden wir die folgenden vier Ansichten haben:

  • Frage-"Index"-Seite - zeigt die neuesten Fragen an.
  • Frage-"Detail"-Seite - zeigt den Frage-Text an, ohne Ergebnisse, aber mit einem Formular zum Abstimmen.
  • Frage-"Ergebnisse"-Seite - zeigt die Ergebnisse für eine bestimmte Frage an.
  • Abstimmungsaktion - behandelt das Abstimmen für eine bestimmte Wahl in einer bestimmten Frage.

In Django werden Webseiten und anderer Inhalt durch Ansichten bereitgestellt. Jede Ansicht wird durch eine Python-Funktion (oder Methode, im Falle von klassenbasierten Ansichten) repräsentiert. Django wird eine Ansicht auswählen, indem es die angeforderte URL untersucht (genauer gesagt den Teil der URL nach dem Domainnamen).

In Ihrer Zeit im Internet haben Sie möglicherweise solche Schönheiten wie ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B kennen gelernt. Sie werden sich freuen zu hören, dass Django uns viel elegantere URL-Muster als das erlaubt.

Ein URL-Muster ist die allgemeine Form einer URL - beispielsweise: /newsarchive/<year>/<month>/.

Um von einer URL zu einer Ansicht zu gelangen, verwendet Django sogenannte 'URL-Konfigurationen'. Eine URL-Konfiguration bildet URL-Muster auf Ansichten ab.

Dieser Tutorial bietet grundlegende Anweisungen zur Verwendung von URL-Konfigurationen, und Sie können sich auf /topics/http/urls für weitere Informationen beziehen.

Weitere Ansichten schreiben

Fügen wir nun einige weitere Ansichten zu polls/views.py hinzu. Diese Ansichten unterscheiden sich etwas, da sie ein Argument entgegennehmen:

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)


def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)


def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

Verknüpfen Sie diese neuen Ansichten mit dem polls.urls-Modul, indem Sie die folgenden ~django.urls.path-Aufrufe hinzufügen:

Bearbeiten Sie die Datei polls/urls.py und fügen Sie die folgenden Zeilen hinzu:

from django.urls import path

from. import views

urlpatterns = [
    ## ex: /polls/
    path("", views.index, name="index"),
    ## ex: /polls/5/
    path("<int:question_id>/", views.detail, name="detail"),
    ## ex: /polls/5/results/
    path("<int:question_id>/results/", views.results, name="results"),
    ## ex: /polls/5/vote/
    path("<int:question_id>/vote/", views.vote, name="vote"),
]

Führen Sie nun den Server erneut aus:

cd ~/project/mysite
python manage.py runserver 0.0.0.0:8080

Wechseln Sie zur Registerkarte Web 8080 bei /polls/34/. Es wird die detail()-Methode ausgeführt und das in der URL angegebene ID angezeigt. Versuchen Sie auch /polls/34/results/ und /polls/34/vote/ - diese werden die Platzhalter-Resultate und Abstimmungsseiten anzeigen.

Django URL routing diagram

Wenn jemand eine Seite von Ihrer Website anfordert - sagen wir /polls/34/, lädt Django das Python-Modul mysite.urls, da es durch die Einstellung ROOT_URLCONF angegeben ist. Es findet die Variable urlpatterns und läuft die Muster in der angegebenen Reihenfolge durch. Nachdem es die Übereinstimmung bei 'polls/' gefunden hat, entfernt es den übereinstimmenden Text ("polls/") und sendet den verbleibenden Text - "34/" - an die URL-Konfiguration von 'polls.urls' für weitere Verarbeitung. Dort stimmt es mit '<int:question_id>/' überein, was zu einem Aufruf der detail()-Ansicht wie folgt führt:

detail(request=<HttpRequest object>, question_id=34)

Der Teil question_id=34 stammt aus <int:question_id>. Mit Hilfe von spitzen Klammern "fangt" man einen Teil der URL und sendet ihn als Schlüsselwortargument an die Ansichtsfunktion. Der Teil question_id der Zeichenfolge definiert den Namen, der verwendet wird, um das übereinstimmende Muster zu identifizieren, und der Teil int ist ein Konverter, der bestimmt, welche Muster diesem Teil des URL-Pfads entsprechen sollten. Das Doppelpunktzeichen (:) trennt den Konverter und den Muster-Namen voneinander.

Ansichten schreiben, die tatsächlich etwas tun

Jede Ansicht ist für eine der beiden folgenden Aufgaben verantwortlich: entweder einen ~django.http.HttpResponse-Objekt zurückzugeben, das den Inhalt der angeforderten Seite enthält, oder eine Ausnahme wie ~django.http.Http404 zu werfen. Der Rest liegt an Ihnen.

Ihre Ansicht kann Datenbankaufzeichnungen lesen oder auch nicht. Sie kann ein Template-System wie das von Django verwenden - oder ein Drittanbieter-Python-Template-System - oder auch nicht. Sie kann eine PDF-Datei generieren, XML ausgeben, eine ZIP-Datei im Lauf der Zeit erstellen, alles, was Sie möchten, mit beliebigen Python-Bibliotheken, die Sie möchten.

Alles, was Django will, ist dieses ~django.http.HttpResponse. Oder eine Ausnahme.

Damit es praktisch ist, verwenden wir die eigene Datenbank-API von Django, die wir im Tutorial 2 behandelt haben. Hier ist eine erste Version einer neuen index()-Ansicht, die die fünf neuesten Umfragen in der System anzeigt, getrennt durch Kommas, gemäß dem Veröffentlichungsdatum:

Bearbeiten Sie die Datei polls/views.py und ändern Sie sie so, dass sie wie folgt aussieht:

from django.http import HttpResponse

from.models import Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    output = ", ".join([q.question_text for q in latest_question_list])
    return HttpResponse(output)


## Lassen Sie die restlichen Ansichten (detail, results, vote) unverändert

Es gibt hier jedoch ein Problem: Die Design der Seite ist im Code der Ansicht festcodiert. Wenn Sie die Art, wie die Seite aussieht, ändern möchten, müssen Sie diesen Python-Code bearbeiten. Deshalb verwenden wir das Template-System von Django, um das Design von Python zu trennen, indem wir ein Template erstellen, das die Ansicht verwenden kann.

Erstellen Sie zunächst ein Verzeichnis namens templates in Ihrem polls-Verzeichnis. Django wird dort nach Templates suchen.

Die TEMPLATES-Einstellung Ihres Projekts beschreibt, wie Django Templates laden und rendern wird. Die Standard-Einstellungsdatei konfiguriert einen DjangoTemplates-Backend, dessen Option APP_DIRS <TEMPLATES-APP_DIRS> auf True gesetzt ist. Konventionell sucht DjangoTemplates in jedem der INSTALLED_APPS ein Unterverzeichnis namens "templates".

Innerhalb des gerade erstellten templates-Verzeichnisses erstellen Sie ein weiteres Verzeichnis namens polls, und innerhalb davon erstellen Sie eine Datei namens index.html. Mit anderen Worten, Ihr Template sollte sich unter polls/templates/polls/index.html befinden. Aufgrund der Art, wie der app_directories-Template-Loader funktioniert, wie oben beschrieben, können Sie auf dieses Template innerhalb von Django als polls/index.html verweisen.

Template-Namensraum

Wir könnten möglicherweise damit auskommen, unsere Templates direkt in polls/templates zu platzieren (anstatt ein weiteres polls-Unterverzeichnis zu erstellen), aber es wäre tatsächlich ein schlechter Gedanke. Django wird das erste Template auswählen, dessen Name übereinstimmt, und wenn Sie ein Template mit demselben Namen in einer anderen Anwendung hätten, wäre Django nicht in der Lage, zwischen ihnen zu unterscheiden.

Wir müssen Django in der Lage sein, auf das richtige zu verweisen, und der beste Weg, dies sicherzustellen, ist, indem wir sie namespacing. Das heißt, indem wir diese Templates in einem anderen Verzeichnis ablegen, das nach der Anwendung selbst benannt ist.

Fügen Sie den folgenden Code in das Template ein:

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

Hinweis:

Um das Tutorial kürzer zu halten, verwenden alle Template-Beispiele unvollständiges HTML. In Ihren eigenen Projekten sollten Sie vollständige HTML-Dokumente verwenden.

Lassen Sie uns nun unsere index-Ansicht in polls/views.py aktualisieren, um das Template zu verwenden:

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

from.models import Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    template = loader.get_template("polls/index.html")
    context = {
        "latest_question_list": latest_question_list,
    }
    return HttpResponse(template.render(context, request))

Dieser Code lädt das Template namens polls/index.html und übergibt ihm einen Kontext. Der Kontext ist ein Dictionary, das Template-Variablennamen auf Python-Objekte abbildet.

Starten Sie den Server erneut:

python manage.py runserver 0.0.0.0:8080

Laden Sie die Seite, indem Sie Ihren Browser auf "/polls/" zeigen, und Sie sollten eine aufzählungsweise aufgelistete Liste sehen, die die Frage "What's up" aus Tutorial 2 enthält. Der Link führt zur Detailseite der Frage.

Django polls index page

Ein Kurzschluss: ~django.shortcuts.render

Es ist eine sehr häufige Vorgehensweise, ein Template zu laden, einen Kontext zu füllen und ein ~django.http.HttpResponse-Objekt mit dem Ergebnis des gerenderten Templates zurückzugeben. Django bietet einen Kurzschluss an. Hier ist die vollständige index()-Ansicht, neu geschrieben:

from django.shortcuts import render

from.models import Question


def index(request):
    latest_question_list = Question.objects.order_by("-pub_date")[:5]
    context = {"latest_question_list": latest_question_list}
    return render(request, "polls/index.html", context)

Beachten Sie, dass wir dies in allen diesen Ansichten getan haben, brauchen wir nicht mehr ~django.template.loader und ~django.http.HttpResponse zu importieren (Sie sollten HttpResponse behalten, wenn Sie immer noch die Stub-Methoden für detail, results und vote haben).

Die ~django.shortcuts.render-Funktion nimmt das Request-Objekt als erstes Argument, einen Template-Namen als zweites Argument und ein Dictionary als optionales drittes Argument entgegen. Sie gibt ein ~django.http.HttpResponse-Objekt des angegebenen Templates zurück, das mit dem angegebenen Kontext gerendert wurde.

Ein 404-Fehler auslösen

Nun wollen wir uns der Frage-Detail-Ansicht widmen - der Seite, die den Frage-Text für eine bestimmte Umfrage anzeigt. Hier ist die Ansicht:

from django.http import Http404
from django.shortcuts import render

from.models import Question


#...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, "polls/detail.html", {"question": question})

Das neue Konzept hier: Die Ansicht wirft die ~django.http.Http404-Ausnahme, wenn eine Frage mit der angeforderten ID nicht existiert.

Wir werden später etwas darüber sprechen, was Sie in das polls/detail.html-Template einfügen könnten, aber wenn Sie das obige Beispiel schnell zum Laufen bringen möchten, reicht eine Datei, die nur enthält:

{{ question }}

Das wird Ihnen für jetzt helfen.

Ein Kurzschluss: ~django.shortcuts.get_object_or_404

Es ist eine sehr häufige Vorgehensweise, ~django.db.models.query.QuerySet.get zu verwenden und ~django.http.Http404 auszulösen, wenn das Objekt nicht existiert. Django bietet einen Kurzschluss an. Hier ist die detail()-Ansicht, neu geschrieben:

from django.shortcuts import get_object_or_404, render

from.models import Question


#...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/detail.html", {"question": question})

Die ~django.shortcuts.get_object_or_404-Funktion nimmt ein Django-Modell als erstes Argument und eine beliebige Anzahl von Schlüsselwortargumenten entgegen, die sie an die ~django.db.models.query.QuerySet.get-Funktion des Modells-Managers weitergibt. Sie wirft ~django.http.Http404, wenn das Objekt nicht existiert.

Warum verwenden wir eine Hilfsfunktion ~django.shortcuts.get_object_or_404 anstatt automatisch die ~django.core.exceptions.ObjectDoesNotExist-Ausnahmen auf einer höheren Ebene zu fangen oder das Model-API ~django.http.Http404 statt ~django.core.exceptions.ObjectDoesNotExist auszulösen?

Weil das die Modellschicht mit der Ansichtsschicht verknüpfen würde. Eines der wichtigsten Designziele von Django ist die Aufrechterhaltung einer lose Kopplung. Einige kontrollierte Kopplung wird im django.shortcuts-Modul eingeführt.

Es gibt auch eine ~django.shortcuts.get_list_or_404-Funktion, die genauso funktioniert wie ~django.shortcuts.get_object_or_404 - nur dass sie ~django.db.models.query.QuerySet.filter statt ~django.db.models.query.QuerySet.get verwendet. Sie wirft ~django.http.Http404, wenn die Liste leer ist.

Das Template-System verwenden

Zurück zur detail()-Ansicht unserer Umfrageanwendung. Gegeben die Kontextvariable question, könnte das polls/detail.html-Template so aussehen:

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

Das Template-System verwendet die Punkt-Such-Syntax, um auf Variable-Attribute zuzugreifen. Im Beispiel von {{ question.question_text }} versucht Django zunächst eine Wörterbuchsuche auf dem Objekt question. Wenn das fehlschlägt, versucht es eine Attributsuche - was in diesem Fall funktioniert. Wenn die Attributsuche fehlgeschlagen wäre, hätte es eine Listenindexsuche versucht.

Methoden-Aufrufe erfolgen in der {% for %}<for>-Schleife: question.choice_set.all wird als Python-Code question.choice_set.all() interpretiert, der ein Iterable von Choice-Objekten zurückgibt und für die Verwendung im {% for %}<for>-Tag geeignet ist.

Entfernen von hartcodierten URLs in Templates

Denken Sie daran, als wir den Link zu einer Frage im polls/index.html-Template geschrieben haben, war der Link teilweise wie folgt hartcodiert:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

Das Problem mit diesem hartcodierten, eng gekoppelten Ansatz ist, dass es schwierig wird, URLs in Projekten mit vielen Templates zu ändern. Da Sie jedoch das name-Argument in den ~django.urls.path-Funktionen im polls.urls-Modul definiert haben, können Sie die Abhängigkeit von bestimmten in Ihren URL-Konfigurationen definierten URL-Pfaden entfernen, indem Sie das {% url %}-Template-Tag verwenden:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

Wie dies funktioniert, besteht darin, die in der polls.urls-Datei definierte URL-Definition aufzurufen. Sie können genau sehen, wo der URL-Name 'detail' definiert ist, wie hier unten:

## der 'name'-Wert, wie er vom {% url %} Template-Tag aufgerufen wird
path("<int:question_id>/", views.detail, name="detail"),

Wenn Sie die URL der Umfrage-Detail-Ansicht auf etwas anderes ändern möchten, vielleicht auf etwas wie polls/specifics/12/ anstatt es im Template (oder Templates) zu tun, würden Sie es in polls/urls.py ändern:

Sie müssen das Template überhaupt nicht ändern.

## das Wort'specifics' hinzugefügt
path("specifics/<int:question_id>/", views.detail, name="detail"),

Namensraum für URL-Namen

Das Tutorial-Projekt hat nur eine App, nämlich polls. In echten Django-Projekten könnten es fünf, zehn, zwanzig Apps oder mehr geben. Wie unterscheidet Django die URL-Namen zwischen ihnen? Beispielsweise hat die polls-App eine detail-Ansicht, und auch eine App auf dem gleichen Projekt, die für einen Blog ist, könnte dies tun. Wie kann man dafür sorgen, dass Django weiß, welche App-Ansicht für eine URL zu erstellen, wenn man das {% url %}-Template-Tag verwendet?

Die Antwort ist, Namensräume zu Ihrem URLconf hinzuzufügen. Öffnen Sie die Datei polls/urls.py und fügen Sie einen app_name hinzu, um den Anwendungs-Namensraum festzulegen:

from django.urls import path

from. import views

app_name = "polls"
urlpatterns = [
    ## z.B.: /polls/
    path("", views.index, name="index"),
    ## z.B.: /polls/5/
    path("<int:question_id>/", views.detail, name="detail"),
    ## z.B.: /polls/5/results/
    path("<int:question_id>/results/", views.results, name="results"),
    ## z.B.: /polls/5/vote/
    path("<int:question_id>/vote/", views.vote, name="vote"),
]

Ändern Sie nun Ihr polls/index.html-Template von:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

um auf die Namensraum-detail-Ansicht zu verweisen:

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
URL namespacing example

Wenn Sie sich mit dem Schreiben von Ansichten vertraut machen, lesen Sie Form Processing and Cutting Down Our Code, um die Grundlagen der Formularverarbeitung und generischer Ansichten zu lernen.

Zusammenfassung

Herzlichen Glückwunsch! Sie haben das Lab "Creating the Public Interface Views" abgeschlossen. Sie können in LabEx weitere Labs ausprobieren, um Ihre Fähigkeiten zu verbessern.