# Django 5.2.8 documentation

# Skriva din första Django-app, del 3

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”

Var du kan få hjälp:

Om du har problem med att gå igenom den här handledningen kan du gå till avsnittet Att få hjälp i FAQ.

## Översikt

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 visningar

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.py`

    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)

Koppla dessa nya vyer till modulen `polls.urls` genom att lägga till följande `path()`-anrop:

`polls/urls.py`

    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"),
    ]

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ågot

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.py`

    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)

    # Leave the rest of the views (detail, results, vote) unchanged

There’s a problem here, though: the page’s design is hardcoded in the view. If you want to change the way the page looks, you’ll have to edit this Python code. So let’s use Django’s template system to separate the design from Python by creating a template that the view can use.

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`.

Namnavstånd för mallar

Nu skulle vi måhända kunna komma undan med att lägga våra mallar direkt i `polls/templates` (snarare än att skapa en annan `polls` underkatalog), men det skulle faktiskt vara en dålig idé. Django kommer att välja den första mallen den hittar vars namn matchar, och om du hade en mall med samma namn i en annan applikation skulle Django inte kunna skilja mellan dem. Vi måste kunna peka Django på rätt mall, och det bästa sättet att säkerställa detta är genom att namespacing dem. Det vill säga genom att placera dessa mallar i en annan katalog som är namngiven för själva applikationen.

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 %}

Observera

För att göra handledningen kortare använder alla mallyxempel ofullständig HTML. I dina egna projekt bör du använda kompletta HTML-dokument.

Låt oss nu uppdatera vår vy `index` i `polls/views.py` så att den använder mallen:

`polls/views.py`

    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))

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()`

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.py`

    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)

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-fel

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.py`

    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})

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()`

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.py`

    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})

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.

Filosofi

Varför använder vi en hjälpfunktion `get_object_or_404()` istället för att automatiskt fånga upp `ObjectDoesNotExist`-undantagen på en högre nivå, eller låta modell-API:et höja `Http404` istället för `ObjectDoesNotExist`?

För det skulle koppla ihop modellagret med visningslagret. Ett av de främsta designmålen för Django är att upprätthålla lös koppling. En viss kontrollerad koppling införs i modulen `django.shortcuts`.

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 mallsystemet

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 mallar

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-namn

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.py`

    from 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.