# Django 6.0.5 documentation

# Inbyggda klassbaserade generiska vyer

Att skriva webbapplikationer kan vara enformigt, eftersom vi upprepar vissa mönster om och om igen. Django försöker ta bort en del av denna monotoni på modell- och mallnivåerna, men webbutvecklare upplever också denna tristess på vynivån.

Djangos generiska vyer utvecklades för att lindra den smärtan. De tar vissa vanliga idiom och mönster som finns i vyutveckling och abstraherar dem så att du snabbt kan skriva vanliga vyer över data utan att behöva skriva för mycket kod.

Vi kan känna igen vissa vanliga uppgifter, som att visa en lista med objekt, och skriva kod som visar en lista med valfritt objekt. Då kan modellen i fråga skickas som ett extra argument till URLconf.

Django levereras med generiska vyer för att göra följande:

- Visar list- och detaljsidor för ett enda objekt. Om vi skulle skapa en applikation för att hantera konferenser så skulle en `TalkListView` och en `RegisteredUserListView` vara exempel på listvyer. En enskild samtalssida är ett exempel på vad vi kallar en ”detalj”-vy.

- Presentera datumbaserade objekt på arkivsidor för år/månad/dag, tillhörande detalj- och ”senaste”-sidor.

- Tillåt användare att skapa, uppdatera och ta bort objekt - med eller utan behörighet.

Sammantaget ger dessa vyer gränssnitt för att utföra de vanligaste uppgifterna som utvecklare stöter på.

## Förlängning av generiska vyer

Det råder ingen tvekan om att generiska vyer kan snabba upp utvecklingen avsevärt. I de flesta projekt kommer det dock ett ögonblick när de generiska vyerna inte längre räcker till. Den vanligaste frågan som ställs av nya Django-utvecklare är faktiskt hur man får generiska vyer att hantera ett bredare spektrum av situationer.

Detta är en av anledningarna till att generiska vyer fick en ny design i 1.3-versionen - tidigare var de vyfunktioner med en förvirrande mängd alternativ; nu, i stället för att skicka in en stor mängd konfiguration i URLconf, är det rekommenderade sättet att utöka generiska vyer att underklassa dem och åsidosätta deras attribut eller metoder.

Med detta sagt har generiska vyer en gräns. Om du har svårt att implementera din vy som en underklass till en generisk vy kan det vara mer effektivt att bara skriva den kod du behöver med hjälp av dina egna klassbaserade eller funktionella vyer.

Fler exempel på generiska vyer finns i vissa tredjepartsapplikationer, eller så kan du skriva dina egna efter behov.

## Generiska vyer av objekt

`TemplateView` är förvisso användbar, men Djangos generiska vyer briljerar verkligen när det gäller att presentera vyer av ditt databasinnehåll. Eftersom det är en så vanlig uppgift kommer Django med en handfull inbyggda generiska vyer för att hjälpa till att generera list- och detaljvyer av objekt.

Låt oss börja med att titta på några exempel på hur man visar en lista med objekt eller ett enskilt objekt.

Vi kommer att använda dessa modeller:

    # models.py
    from django.db import models

    class Publisher(models.Model):
        name = models.CharField(max_length=30)
        address = models.CharField(max_length=50)
        city = models.CharField(max_length=60)
        state_province = models.CharField(max_length=30)
        country = models.CharField(max_length=50)
        website = models.URLField()

        class Meta:
            ordering = ["-name"]

        def __str__(self):
            return self.name

    class Author(models.Model):
        salutation = models.CharField(max_length=10)
        name = models.CharField(max_length=200)
        email = models.EmailField()
        headshot = models.ImageField(upload_to="author_headshots")

        def __str__(self):
            return self.name

    class Book(models.Model):
        title = models.CharField(max_length=100)
        authors = models.ManyToManyField("Author")
        publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
        publication_date = models.DateField()

Nu måste vi definiera en vy:

    # views.py
    from django.views.generic import ListView
    from books.models import Publisher

    class PublisherListView(ListView):
        model = Publisher

Koppla slutligen den vyn till dina webbadresser:

    # urls.py
    from django.urls import path
    from books.views import PublisherListView

    urlpatterns = [
        path("publishers/", PublisherListView.as_view()),
    ]

Det är all Python-kod vi behöver skriva. Vi behöver dock fortfarande skriva en mall. Vi kan uttryckligen berätta för vyn vilken mall som ska användas genom att lägga till ett `template_name`-attribut till vyn, men i avsaknad av en uttrycklig mall kommer Django att härleda en från objektets namn. I det här fallet kommer den härledda mallen att vara `"books/publisher_list.html"` - ”books”-delen kommer från namnet på appen som definierar modellen, medan ”publisher”-biten är den gemena versionen av modellens namn.

Observera

Således, när (till exempel) alternativet `APP_DIRS` för en `DjangoTemplates` backend är inställt på True i `TEMPLATES`, kan en mallplats vara: /path/to/project/books/templates/books/publisher_list.html

Denna mall kommer att renderas mot ett sammanhang som innehåller en variabel som heter `object_list` som innehåller alla utgivarobjekt. En mall kan se ut så här:

    {% extends "base.html" %}

    {% block content %}
        <h2>Publishers</h2>
        <ul>
            {% for publisher in object_list %}
                <li>{{ publisher.name }}</li>
            {% endfor %}
        </ul>
    {% endblock %}

Det är verkligen allt det är. Alla de häftiga funktionerna i generiska vyer kommer från att ändra de attribut som ställts in på den generiska vyn. I generic views reference dokumenteras alla generiska vyer och deras alternativ i detalj; resten av det här dokumentet kommer att behandla några av de vanligaste sätten du kan anpassa och utöka generiska vyer.

### Skapa ”vänliga” mallkontexter

Du kanske har lagt märke till att vår mall för förlagslistan lagrar alla förlag i en variabel med namnet ”object_list”. Även om detta fungerar bra är det inte så ”vänligt” mot mallförfattare: de måste ”bara veta” att de har att göra med utgivare här.

Tja, om du har att göra med ett modellobjekt är detta redan gjort åt dig. När du har att göra med ett objekt eller en queryset kan Django fylla i kontexten med den gemena versionen av modellklassens namn. Detta tillhandahålls utöver standardposten `object_list`, men innehåller exakt samma data, dvs. `publisher_list`.

Om detta fortfarande inte är en bra matchning kan du manuellt ställa in namnet på kontextvariabeln. Attributet `context_object_name` på en generisk vy anger vilken kontextvariabel som ska användas:

    # views.py
    from django.views.generic import ListView
    from books.models import Publisher

    class PublisherListView(ListView):
        model = Publisher
        context_object_name = "my_favorite_publishers"

Att tillhandahålla ett användbart `context_object_name` är alltid en bra idé. Dina kollegor som designar mallar kommer att tacka dig.

### Lägga till extra sammanhang

Ofta behöver du presentera lite extra information utöver den som tillhandahålls av den generiska vyn. Tänk till exempel på att visa en lista över alla böcker på varje förlags detaljsida. Den generiska vyn `DetailView` ger utgivaren till sammanhanget, men hur får vi ytterligare information i den mallen?

Svaret är att underordna `DetailView` och tillhandahålla din egen implementering av metoden `get_context_data`. Standardimplementeringen lägger till objektet som visas i mallen, men du kan åsidosätta den för att skicka mer:

    from django.views.generic import DetailView
    from books.models import Book, Publisher

    class PublisherDetailView(DetailView):
        model = Publisher

        def get_context_data(self, **kwargs):
            # Call the base implementation first to get a context
            context = super().get_context_data(**kwargs)
            # Add in a QuerySet of all the books
            context["book_list"] = Book.objects.all()
            return context

Observera

I allmänhet kommer `get_context_data` att sammanfoga kontextdata för alla överordnade klasser med den aktuella klassens data. För att bevara detta beteende i dina egna klasser där du vill ändra kontexten, bör du se till att anropa `get_context_data` på superklassen. När inga två klasser försöker definiera samma nyckel kommer detta att ge de förväntade resultaten. Men om någon klass försöker åsidosätta en nyckel efter att föräldraklasserna har ställt in den (efter anropet till super), måste alla barn till den klassen också uttryckligen ställa in den efter super om de vill vara säkra på att åsidosätta alla föräldrar. Om du har problem kan du se över metodupplösningsordningen för din vy.

Ett annat övervägande är att kontextdata från klassbaserade generiska vyer kommer att åsidosätta data som tillhandahålls av kontextprocessorer; se `get_context_data()` för ett exempel.

### Visning av delmängder av objekt

Låt oss nu ta en närmare titt på argumentet `model` som vi har använt hela tiden. Argumentet `model`, som anger den databasmodell som vyn ska arbeta med, är tillgängligt för alla generiska vyer som arbetar med ett enda objekt eller en samling objekt. Argumentet `model` är dock inte det enda sättet att ange de objekt som vyn ska arbeta med - du kan också ange listan över objekt med argumentet `queryset`:

    from django.views.generic import DetailView
    from books.models import Publisher

    class PublisherDetailView(DetailView):
        context_object_name = "publisher"
        queryset = Publisher.objects.all()

Att ange `model = Publisher` är en förkortning för att säga `queryset = Publisher.objects.all()`. Men genom att använda `queryset` för att definiera en filtrerad lista över objekt kan du vara mer specifik om de objekt som kommer att synas i vyn (se Göra förfrågningar för mer information om `QuerySet`-objekt, och se class-based views reference för fullständiga detaljer).

För att ta ett exempel kanske vi vill sortera en lista med böcker efter utgivningsdatum, med den senaste först:

    from django.views.generic import ListView
    from books.models import Book

    class BookListView(ListView):
        queryset = Book.objects.order_by("-publication_date")
        context_object_name = "book_list"

Det är ett ganska minimalt exempel, men det illustrerar idén på ett bra sätt. Du vill vanligtvis göra mer än att bara ordna om objekt. Om du vill presentera en lista över böcker från ett visst förlag kan du använda samma teknik:

    from django.views.generic import ListView
    from books.models import Book

    class AcmeBookListView(ListView):
        context_object_name = "book_list"
        queryset = Book.objects.filter(publisher__name="ACME Publishing")
        template_name = "books/acme_list.html"

Lägg märke till att vi tillsammans med ett filtrerat `queryset` också använder ett anpassat mallnamn. Om vi inte gjorde det skulle den generiska vyn använda samma mall som ”vanilj”-objektlistan, vilket kanske inte är vad vi vill.

Observera också att det här inte är ett särskilt elegant sätt att göra förlagsspecifika böcker. Om vi vill lägga till ytterligare en förlagssida skulle vi behöva ytterligare en handfull rader i URLconf, och mer än ett fåtal förlag skulle bli orimliga. Vi tar itu med det här problemet i nästa avsnitt.

Observera

If you get a 404 when requesting `/books/acme/`, check to ensure you actually have a Publisher with the name ’ACME Publishing’. Generic views have an `allow_empty` parameter for this case. See the class-based-views reference for more details.

### Dynamisk filtrering

Another common need is to filter down the objects given in a list page by some key in the URL. Earlier we hardcoded the publisher’s name in the URLconf, but what if we wanted to write a view that displayed all the books by some arbitrary publisher?

Praktiskt nog har `ListView` en `get_queryset()`-metod som vi kan åsidosätta. Som standard returnerar den värdet på attributet `queryset`, men vi kan använda det för att lägga till mer logik.

Den viktigaste delen för att få detta att fungera är att när klassbaserade vyer anropas lagras olika användbara saker på `self`; förutom begäran (`self.request`) inkluderar detta de positionella (`self.args`) och namnbaserade (`self.kwargs`) argumenten som fångas enligt URLconf.

Här har vi en URLconf med en enda fångad grupp:

    # urls.py
    from django.urls import path
    from books.views import PublisherBookListView

    urlpatterns = [
        path("books/<publisher>/", PublisherBookListView.as_view()),
    ]

Nu ska vi skriva själva vyn `PublisherBookListView`:

    # views.py
    from django.shortcuts import get_object_or_404
    from django.views.generic import ListView
    from books.models import Book, Publisher

    class PublisherBookListView(ListView):
        template_name = "books/books_by_publisher.html"

        def get_queryset(self):
            self.publisher = get_object_or_404(Publisher, name=self.kwargs["publisher"])
            return Book.objects.filter(publisher=self.publisher)

Att använda `get_queryset` för att lägga till logik i queryset-urvalet är lika bekvämt som det är kraftfullt. Om vi till exempel vill kan vi använda `self.request.user` för att filtrera med hjälp av den aktuella användaren eller annan mer komplex logik.

Vi kan också lägga till utgivaren i sammanhanget samtidigt, så att vi kan använda den i mallen:

    # ...

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        # Add in the publisher
        context["publisher"] = self.publisher
        return context

### Utföra extra arbete

Det sista vanliga mönstret vi ska titta på innebär att man gör lite extra arbete före eller efter anropet av den generiska vyn.

Tänk dig att vi hade ett `last_accessed` fält på vår `Author` modell som vi använde för att hålla reda på den sista gången någon tittade på den författaren:

    # models.py
    from django.db import models

    class Author(models.Model):
        salutation = models.CharField(max_length=10)
        name = models.CharField(max_length=200)
        email = models.EmailField()
        headshot = models.ImageField(upload_to="author_headshots")
        last_accessed = models.DateTimeField()

Den generiska klassen `DetailView` vet ingenting om det här fältet, men återigen kan vi skriva en anpassad vy för att hålla fältet uppdaterat.

Först måste vi lägga till en författardetaljbit i URLconf för att peka på en anpassad vy:

    from django.urls import path
    from books.views import AuthorDetailView

    urlpatterns = [
        # ...
        path("authors/<int:pk>/", AuthorDetailView.as_view(), name="author-detail"),
    ]

Sedan skriver vi vår nya vy – `get_object` är den metod som hämtar objektet – så vi åsidosätter den och omsluter anropet:

    from django.utils import timezone
    from django.views.generic import DetailView
    from books.models import Author

    class AuthorDetailView(DetailView):
        queryset = Author.objects.all()

        def get_object(self):
            obj = super().get_object()
            # Record the last accessed date
            obj.last_accessed = timezone.now()
            obj.save()
            return obj

Observera

URLconf här använder den namngivna gruppen `pk` - detta namn är det standardnamn som `DetailView` använder för att hitta värdet på den primära nyckel som används för att filtrera frågeuppsättningen.

Om du vill kalla gruppen något annat kan du ställa in `pk_url_kwarg` på vyn.