Skriva din första Django-app, del 3Link to this heading
Denna tutorial börjar där Tutorial 2 slutade. Vi fortsätter med web-poll applikationen och fokuserar på att skapa det publika gränssnittet – ”views”
ÖversiktLink to this heading
En vy är en ”typ” av webbsida i din Django-applikation som i allmänhet tjänar en specifik funktion och har en specifik mall. I en bloggapplikation kan du till exempel ha följande vyer:
Bloggens startsida - visar de senaste inläggen.
Entry ”detail” page – permalänksida för en enskild post.
Årsbaserad arkivsida - visar alla månader med poster under det angivna året.
Månadsbaserad arkivsida - visar alla dagar med poster i den angivna månaden.
Dagbaserad arkivsida - visar alla poster under den aktuella dagen.
Comment action – hanterar kommentarer till en viss post.
I vår poll-applikation kommer vi att ha följande fyra vyer:
Fråga ”index”-sida - visar de senaste frågorna.
Fråga ”detalj”-sida – visar en frågetext, utan resultat men med ett formulär för att rösta.
Frågans resultatsida - visar resultaten för en viss fråga.
Vote action – hanterar röstning för ett visst val i en viss fråga.
I Django levereras webbsidor och annat innehåll med hjälp av vyer. Varje vy representeras av en Python-funktion (eller metod, i fallet med klassbaserade vyer). Django väljer en vy genom att undersöka den URL som begärs (för att vara exakt, den del av URL:en som kommer efter domännamnet).
Nu under din tid på webben kanske du har stött på sådana skönheter som ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B. Du kommer att bli glad över att veta att Django tillåter oss mycket mer eleganta URL-mönster än så.
Ett URL-mönster är den allmänna formen av en URL - till exempel: /newsarchive/<year>/<month>/.
För att komma från en URL till en vy använder Django så kallade ”URLconfs”. En URLconf mappar URL-mönster till vyer.
Den här handledningen ger grundläggande instruktioner om användningen av URLconfs, och du kan läsa URL-distributör för mer information.
Skriva fler visningarLink to this heading
Låt oss nu lägga till några fler vyer i polls/views.py. Dessa vyer är lite annorlunda, eftersom de tar ett argument:
polls/views.pydef 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)
Koppla dessa nya vyer till modulen polls.urls genom att lägga till följande path()-anrop:
polls/urls.pyfrom 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"),
]
Ta en titt i din webbläsare, på ”/polls/34/”. Den kommer att köra funktionen detail() och visa det ID som du anger i URL:en. Prova också ”/polls/34/results/” och ”/polls/34/vote/” - dessa kommer att visa platshållarresultaten och röstningssidorna.
När någon begär en sida från din webbplats - säg, ”/polls/34/”, kommer Django att ladda Python-modulen mysite.urls eftersom den pekas ut av inställningen ROOT_URLCONF. Den hittar variabeln med namnet urlpatterns och går igenom mönstren i ordning. Efter att ha hittat matchningen vid 'polls/', tar den bort den matchande texten ("polls/") och skickar den återstående texten – "34/" – till URLconf ’polls.urls’ för vidare bearbetning. Där matchar den '<int:question_id>/', vilket resulterar i ett anrop till vyn detail() så här:
detail(request=<HttpRequest object>, question_id=34)
Delen question_id=34 kommer från <int:question_id>. Med hjälp av vinkelparenteser ”fångas” en del av URL:en och skickas som ett nyckelordsargument till view-funktionen. Delen question_id i strängen definierar det namn som ska användas för att identifiera det matchade mönstret, och delen int är en omvandlare som avgör vilka mönster som ska matcha den här delen av URL-sökvägen. Kolonet (:) skiljer omvandlaren och mönsternamnet åt.
Skriv åsikter som faktiskt gör någotLink to this heading
Varje vy är ansvarig för att göra en av två saker: returnera ett HttpResponse-objekt som innehåller innehållet för den begärda sidan, eller skapa ett undantag som Http404. Resten är upp till dig.
Din vy kan läsa poster från en databas, eller inte. Den kan använda ett mallsystem som Djangos - eller ett Python-mallsystem från tredje part - eller inte. Den kan generera en PDF-fil, mata ut XML, skapa en ZIP-fil i farten, vad du vill, med hjälp av vilka Python-bibliotek du vill.
Allt Django vill ha är den där HttpResponse. Eller ett undantag.
Eftersom det är bekvämt, låt oss använda Djangos egen databas API, som vi täckte i Tutorial 2. Här är ett försök till en ny index() vy, som visar de senaste 5 omröstningsfrågorna i systemet, åtskilda med kommatecken, enligt publiceringsdatum:
polls/views.pyfrom 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)
# Leave the rest of the views (detail, results, vote) unchanged
Det finns dock ett problem här: sidans design är hårdkodad i vyn. Om du vill ändra hur sidan ser ut måste du redigera denna Python-kod. Så låt oss använda Djangos mallsystem för att separera designen från Python genom att skapa en mall som vyn kan använda.
Först skapar du en katalog som heter templates i din polls-katalog. Django kommer att leta efter mallar där.
Ditt projekts TEMPLATES-inställning beskriver hur Django ska ladda och rendera mallar. Standardinställningsfilen konfigurerar en DjangoTemplates-backend vars APP_DIRS-alternativ är inställt på True. Enligt konvention letar DjangoTemplates efter en ”templates”-underkatalog i varje INSTALLED_APPS.
I katalogen templates som du just har skapat, skapar du en annan katalog som heter polls, och i den skapar du en fil som heter index.html. Med andra ord bör din mall ligga på polls/templates/polls/index.html. På grund av hur mallladdaren app_directories fungerar enligt beskrivningen ovan, kan du hänvisa till denna mall inom Django som polls/index.html.
Lägg in följande kod i den mallen:
polls/templates/polls/index.html{% 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 %}
Låt oss nu uppdatera vår vy index i polls/views.py så att den använder mallen:
polls/views.pyfrom 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))
Den koden laddar mallen som heter polls/index.html och skickar en kontext till den. Kontexten är en ordbok som mappar mallens variabelnamn till Python-objekt.
Ladda sidan genom att rikta webbläsaren mot ”/polls/”, och du bör se en punktlista som innehåller ”What’s up”-frågan från Tutorial 2. Länken pekar till frågans detaljsida.
En genväg: render()Link to this heading
Det är ett mycket vanligt idiom att ladda en mall, fylla en kontext och returnera ett HttpResponse-objekt med resultatet av den renderade mallen. Django tillhandahåller en genväg. Här är den fullständiga index()-vyn, omskriven:
polls/views.pyfrom 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)
Observera att när vi har gjort detta i alla dessa vyer behöver vi inte längre importera loader och HttpResponse (du vill behålla HttpResponse om du fortfarande har stubmetoderna för detail, results och vote).
Funktionen render() tar request-objektet som sitt första argument, ett mallnamn som sitt andra argument och en ordbok som sitt valfria tredje argument. Den returnerar ett HttpResponse-objekt av den givna mallen renderad med den givna kontexten.
Skapa ett 404-felLink to this heading
Låt oss nu ta itu med vyn för frågedetaljer - den sida som visar frågetexten för en viss undersökning. Här är vyn:
polls/views.pyfrom 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})
Det nya konceptet här: Vyn ger upphov till undantaget Http404 om en fråga med det begärda ID:t inte finns.
Vi kommer att diskutera vad du kan lägga in i mallen polls/detail.html lite senare, men om du snabbt vill få ovanstående exempel att fungera, kan en fil som bara innehåller:
polls/templates/polls/detail.html{{ question }}
kommer att hjälpa dig att komma igång för tillfället.
En genväg: get_object_or_404()Link to this heading
Det är ett mycket vanligt idiom att använda get() och få Http404 om objektet inte finns. Django tillhandahåller en genväg. Här är detail() vyn, omskriven:
polls/views.pyfrom 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})
Funktionen get_object_or_404() tar en Django-modell som sitt första argument och ett godtyckligt antal nyckelordsargument, som den skickar till funktionen get() i modellens manager. Den ger upphov till Http404 om objektet inte finns.
Det finns också en funktion get_list_or_404(), som fungerar precis som get_object_or_404() - förutom att den använder filter() istället för get(). Det ger upphov till Http404 om listan är tom.
Använd mallsystemetLink to this heading
Tillbaka till detail()-vyn för vår poll-applikation. Med tanke på kontextvariabeln question, så här kan mallen polls/detail.html se ut:
polls/templates/polls/detail.html<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
Mallsystemet använder dot-lookup-syntax för att komma åt variabelattribut. I exemplet med {{ question.question_text }} gör Django först en ordboksuppslagning på objektet question. Om det misslyckas försöker den med en attributuppslagning - vilket fungerar i det här fallet. Om attributuppslagningen hade misslyckats skulle den ha försökt med en listindexuppslagning.
Metoduppringning sker i {% feller %}-loopen: question.choice_set.all tolkas som Python-koden question.choice_set.all(), som returnerar en iterabel av Choice-objekt och är lämplig för användning i taggen {% feller %}.
Se Mallguide för mer information om mallar.
Ta bort hårdkodade webbadresser i mallarLink to this heading
Kom ihåg att när vi skrev länken till en fråga i mallen polls/index.html, var länken delvis hårdkodad så här:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
Problemet med detta hårdkodade, tätt kopplade tillvägagångssätt är att det blir svårt att ändra webbadresser i projekt med många mallar. Men eftersom du definierade argumentet name i path()-funktionerna i modulen polls.urls, kan du ta bort beroendet av specifika URL-sökvägar som definieras i dina url-konfigurationer genom att använda malltaggen {% url %}:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
Det här fungerar genom att leta upp URL-definitionen som anges i modulen polls.urls. Du kan se exakt var URL-namnet ”detail” är definierat nedan:
...
# the 'name' value as called by the {% url %} template tag
path("<int:question_id>/", views.detail, name="detail"),
...
Om du vill ändra webbadressen till detaljvyn för omröstningar till något annat, kanske till något som polls/specifics/12/ istället för att göra det i mallen (eller mallarna) skulle du ändra den i polls/urls.py:
...
# added the word 'specifics'
path("specifics/<int:question_id>/", views.detail, name="detail"),
...
Namngivning av URL-namnLink to this heading
Självstudieprojektet har bara en app, polls. I riktiga Django-projekt kan det finnas fem, tio, tjugo appar eller fler. Hur skiljer Django URL-namnen mellan dem? Till exempel har appen polls en detail vy, och det kan också en app på samma projekt som är för en blogg. Hur gör man så att Django vet vilken appvy som ska skapas för en webbadress när man använder malltaggen {% url %}?
Svaret är att lägga till namnrymder i din URLconf. I filen polls/urls.py, gå vidare och lägg till en app_name för att ställa in applikationens namnrymd:
polls/urls.pyfrom django.urls import path
from . import views
app_name = "polls"
urlpatterns = [
path("", views.index, name="index"),
path("<int:question_id>/", views.detail, name="detail"),
path("<int:question_id>/results/", views.results, name="results"),
path("<int:question_id>/vote/", views.vote, name="vote"),
]
Ändra nu din polls/index.html mall från:
polls/templates/polls/index.html<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
för att peka på den namngivna detaljvyn:
polls/templates/polls/index.html<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
När du känner dig bekväm med att skriva vyer kan du läsa del 4 i denna handledning för att lära dig grunderna om formulärbehandling och generiska vyer.