# Django 5.2.8 documentation

# Använda mixins med klassbaserade vyer

Varning

This is an advanced topic. A working knowledge of Django’s class-based views is advised before exploring these techniques.

Djangos inbyggda klassbaserade vyer ger en hel del funktionalitet, men en del av den kanske du vill använda separat. Till exempel kanske du vill skriva en vy som renderar en mall för att skapa HTTP-svaret, men du kan inte använda `TemplateView`; kanske behöver du bara rendera en mall på `POST`, med `GET` som gör något helt annat. Även om du kan använda `TemplateResponse` direkt, kommer detta sannolikt att resultera i duplicerad kod.

Av denna anledning tillhandahåller Django också ett antal mixins som ger mer diskret funktionalitet. Mallrendering, till exempel, är inkapslad i `TemplateResponseMixin`. Djangos referensdokumentation innehåller full dokumentation av alla mixins.

## Sammanhang och svarsmallar

Två centrala mixins tillhandahålls som hjälper till att tillhandahålla ett konsekvent gränssnitt för att arbeta med mallar i klassbaserade vyer.

`TemplateResponseMixin`

  Every built in view which returns a `TemplateResponse` will call the `render_to_response()` method that `TemplateResponseMixin` provides. Most of the time this will be called for you (for instance, it is called by the `get()` method implemented by both `TemplateView` and `DetailView`); similarly, it’s unlikely that you’ll need to override it, although if you want your response to return something not rendered via a Django template then you’ll want to do it. For an example of this, see the JSONResponseMixin example.

  `render_to_response()` själv anropar `get_template_names()`, som standard kommer att leta upp `template_name` på den klassbaserade vyn; två andra mixins (`SingleObjectTemplateResponseMixin` och `MultipleObjectTemplateResponseMixin`) åsidosätter detta för att ge mer flexibla standardvärden när man hanterar faktiska objekt.

`ContextMixin`

  Every built in view which needs context data, such as for rendering a template (including `TemplateResponseMixin` above), should call `get_context_data()` passing any data they want to ensure is in there as keyword arguments. `get_context_data()` returns a dictionary; in `ContextMixin` it returns its keyword arguments, but it is common to override this to add more members to the dictionary. You can also use the `extra_context` attribute.

## Uppbyggnad av Djangos generiska klassbaserade vyer

Låt oss titta på hur två av Djangos generiska klassbaserade vyer är uppbyggda av mixins som ger diskret funktionalitet. Vi kommer att överväga `DetailView`, som renderar en ”detaljerad” vy av ett objekt, och `ListView`, som kommer att rendera en lista över objekt, vanligtvis från en queryset, och eventuellt paginera dem. Detta kommer att introducera oss till fyra mixins som tillsammans ger användbar funktionalitet när man arbetar med antingen ett enda Django-objekt eller flera objekt.

Det finns också mixins involverade i de generiska redigeringsvyerna (`FormView`, och de modellspecifika vyerna `CreateView`, `UpdateView` och `DeleteView`), och i de datumbaserade generiska vyerna. Dessa behandlas i mixin referensdokumentation.

### `DetailView`: att arbeta med ett enda Django-objekt

För att visa detaljerna i ett objekt måste vi i princip göra två saker: vi måste leta upp objektet och sedan måste vi skapa en `TemplateResponse` med en lämplig mall och det objektet som kontext.

To get the object, `DetailView` relies on `SingleObjectMixin`, which provides a `get_object()` method that figures out the object based on the URL of the request (it looks for `pk` and `slug` keyword arguments as declared in the URLConf, and looks the object up either from the `model` attribute on the view, or the `queryset` attribute if that’s provided). `SingleObjectMixin` also overrides `get_context_data()`, which is used across all Django’s built in class-based views to supply context data for template renders.

To then make a `TemplateResponse`, `DetailView` uses `SingleObjectTemplateResponseMixin`, which extends `TemplateResponseMixin`, overriding `get_template_names()` as discussed above. It actually provides a fairly sophisticated set of options, but the main one that most people are going to use is `<app_label>/<model_name>_detail.html`. The `_detail` part can be changed by setting `template_name_suffix` on a subclass to something else. (For instance, the generic edit views use `_form` for create and update views, and `_confirm_delete` for delete views.)

### `ListView`: arbeta med många Django-objekt

Listor med objekt följer ungefär samma mönster: vi behöver en (eventuellt paginerad) lista med objekt, vanligtvis en `QuerySet`, och sedan måste vi skapa en `TemplateResponse` med en lämplig mall som använder den listan med objekt.

För att få objekten använder `ListView` `MultipleObjectMixin`, som tillhandahåller både `get_queryset()` och `paginate_queryset()`. Till skillnad från `SingleObjectMixin` finns det inget behov av att ange delar av URL:en för att räkna ut vilken queryset som ska användas, så standardvärdet använder attributet `queryset` eller `model` på vyklassen. En vanlig anledning att åsidosätta `get_queryset()` här skulle vara att dynamiskt variera objekten, till exempel beroende på den aktuella användaren eller att utesluta inlägg i framtiden för en blogg.

`MultipleObjectMixin` also overrides `get_context_data()` to include appropriate context variables for pagination (providing dummies if pagination is disabled). It relies on `object_list` being passed in as a keyword argument, which `ListView` arranges for it.

För att göra en `TemplateResponse`, använder `ListView` sedan `MultipleObjectTemplateResponseMixin`; som med `SingleObjectTemplateResponseMixin` ovan, åsidosätter detta `get_template_names()` för att tillhandahålla `en rad alternativ`, där det vanligaste är `<app_label>/<model_name>_list.html`, där `_list`-delen återigen hämtas från attributet `template_name_suffix`. (De datumbaserade generiska vyerna använder suffix som `_archive`, `_archive_year` och så vidare för att använda olika mallar för de olika specialiserade datumbaserade listvyerna)

## Använda Djangos klassbaserade vy-mixins

Nu har vi sett hur Djangos generiska klassbaserade vyer använder de medföljande mixinerna, låt oss titta på andra sätt vi kan kombinera dem. Vi kommer fortfarande att kombinera dem med antingen inbyggda klassbaserade vyer eller andra generiska klassbaserade vyer, men det finns en rad sällsynta problem du kan lösa än vad som tillhandahålls av Django från början.

Varning

Alla mixins kan inte användas tillsammans, och alla generiska klassbaserade vyer kan inte användas med alla andra mixins. Här presenterar vi några exempel som fungerar; om du vill sammanföra annan funktionalitet måste du ta hänsyn till interaktioner mellan attribut och metoder som överlappar mellan de olika klasserna du använder, och hur method resolution order påverkar vilka versioner av metoderna som ska anropas i vilken ordning.

Referensdokumentationen för Djangos class-based views och class-based view mixins hjälper dig att förstå vilka attribut och metoder som sannolikt kommer att orsaka konflikt mellan olika klasser och mixins.

Om du är osäker är det ofta bättre att backa och basera ditt arbete på `View` eller `TemplateView`, kanske med `SingleObjectMixin` och `MultipleObjectMixin`. Även om du förmodligen kommer att skriva mer kod, är det mer sannolikt att den är tydligt förståelig för någon annan som kommer till den senare, och med färre interaktioner att oroa sig för sparar du dig själv en del tänkande. (Naturligtvis kan du alltid dyka in i Djangos implementering av de generiska klassbaserade vyerna för inspiration om hur du ska ta itu med problem)

### Använda `SingleObjectMixin` med View

Om vi vill skriva en klassbaserad vy som bara svarar på `POST`, subklassar vi `View` och skriver en `post()`-metod i subklassen. Men om vi vill att vår bearbetning ska fungera på ett visst objekt, identifierat från URL, vill vi ha den funktionalitet som tillhandahålls av `SingleObjectMixin`.

We’ll demonstrate this with the `Author` model we used in the generic class-based views introduction.

`Vyer.py`

    from django.http import HttpResponseForbidden, HttpResponseRedirect
    from django.urls import reverse
    from django.views import View
    from django.views.generic.detail import SingleObjectMixin
    from books.models import Author

    class RecordInterestView(SingleObjectMixin, View):
        """Records the current user's interest in an author."""

        model = Author

        def post(self, request, *args, **kwargs):
            if not request.user.is_authenticated:
                return HttpResponseForbidden()

            # Look up the author we're interested in.
            self.object = self.get_object()
            # Actually record interest somehow here!

            return HttpResponseRedirect(
                reverse("author-detail", kwargs={"pk": self.object.pk})
            )

I praktiken skulle du förmodligen vilja registrera intresset i en nyckelvärdesbutik snarare än i en relationsdatabas, så vi har utelämnat den biten. Den enda delen av vyn som behöver oroa sig för att använda `SingleObjectMixin` är där vi vill leta upp den författare vi är intresserade av, vilket den gör med ett anrop till `self.get_object()`. Allt annat tas om hand för oss av mixin.

Vi kan enkelt koppla in detta i våra webbadresser:

`urls.py`

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

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

Notera gruppen med namnet `pk`, som `get_object()` använder för att leta upp instansen `Author`. Du kan också använda en slug, eller någon av de andra funktionerna i `SingleObjectMixin`.

### Använda `SingleObjectMixin` med `ListView`

`ListView` ger inbyggd paginering, men du kanske vill paginera en lista med objekt som alla är länkade (med en främmande nyckel) till ett annat objekt. I vårt publiceringsexempel kanske du vill paginera genom alla böcker från en viss utgivare.

Ett sätt att göra detta är att kombinera `ListView` med `SingleObjectMixin`, så att queryset för den paginerade listan med böcker kan hänga på förlaget som hittas som ett enda objekt. För att kunna göra detta måste vi ha två olika querysets:

`Book` queryset för användning av `ListView`

  Eftersom vi har tillgång till det förlag vars böcker vi vill lista, åsidosätter vi `get_queryset()` och använder förlagets reverse foreign key manager.

`Publisher` queryset for use in `get_object()`

  Vi kommer att förlita oss på standardimplementeringen av `get_object()` för att hämta rätt `Publisher`-objekt. Vi måste dock uttryckligen skicka ett `queryset`-argument eftersom standardimplementeringen av `get_object()` annars skulle anropa `get_queryset()` som vi har åsidosatt för att returnera `Book`-objekt istället för `Publisher`-objekt.

Observera

Vi måste tänka noga på `get_context_data()`. Eftersom både `SingleObjectMixin` och `ListView` kommer att lägga saker i kontextdata under värdet av `context_object_name` om det är inställt, kommer vi istället att uttryckligen se till att `Publisher` finns i kontextdata. `ListView` kommer att lägga till lämplig `page_obj` och `paginator` åt oss förutsatt att vi kommer ihåg att anropa `super()`.

Nu kan vi skriva en ny `PublisherDetailView`:

    from django.views.generic import ListView
    from django.views.generic.detail import SingleObjectMixin
    from books.models import Publisher

    class PublisherDetailView(SingleObjectMixin, ListView):
        paginate_by = 2
        template_name = "books/publisher_detail.html"

        def get(self, request, *args, **kwargs):
            self.object = self.get_object(queryset=Publisher.objects.all())
            return super().get(request, *args, **kwargs)

        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            context["publisher"] = self.object
            return context

        def get_queryset(self):
            return self.object.book_set.all()

Lägg märke till hur vi anger `self.object` inom `get()` så att vi kan använda det igen senare i `get_context_data()` och `get_queryset()`. Om du inte anger `template_name`, kommer mallen som standard att välja det normala `ListView`-valet, som i det här fallet skulle vara `"books/book_list.html"` eftersom det är en lista över böcker; `ListView` vet ingenting om `SingleObjectMixin``, så den har ingen aning om att den här vyn har något att göra med en `Publisher`.

`paginate_by` är avsiktligt liten i exemplet så att du inte behöver skapa massor av böcker för att se att pagineringen fungerar! Här är den mall du vill använda:

    {% extends "base.html" %}

    {% block content %}
        <h2>Publisher {{ publisher.name }}</h2>

        <ol>
          {% for book in page_obj %}
            <li>{{ book.title }}</li>
          {% endfor %}
        </ol>

        <div class="pagination">
            <span class="step-links">
                {% if page_obj.has_previous %}
                    <a href="?page={{ page_obj.previous_page_number }}">previous</a>
                {% endif %}

                <span class="current">
                    Page {{ page_obj.number }} of {{ paginator.num_pages }}.
                </span>

                {% if page_obj.has_next %}
                    <a href="?page={{ page_obj.next_page_number }}">next</a>
                {% endif %}
            </span>
        </div>
    {% endblock %}

## Undvik allt som är mer komplicerat

Generellt kan du använda `TemplateResponseMixin` och `SingleObjectMixin` när du behöver deras funktionalitet. Som visas ovan kan du med lite försiktighet till och med kombinera `SingleObjectMixin` med `ListView`. Saker och ting blir dock alltmer komplexa när du försöker göra det, och en bra tumregel är:

Råd

Each of your views should use only mixins or views from one of the groups of generic class-based views: detail, list, editing and date. For example it’s fine to combine `TemplateView` (built in view) with `MultipleObjectMixin` (generic list), but you’re likely to have problems combining `SingleObjectMixin` (generic detail) with `MultipleObjectMixin` (generic list).

För att visa vad som händer när man försöker bli mer sofistikerad, visar vi ett exempel som offrar läsbarhet och underhåll när det finns en enklare lösning. Låt oss först titta på ett naivt försök att kombinera `DetailView` med `FormMixin` för att göra det möjligt för oss att `POST` en Django `Form` till samma URL som vi visar ett objekt med `DetailView`.

### Använda `FormMixin` med `DetailView`

Tänk tillbaka på vårt tidigare exempel där vi använde `View` och `SingleObjectMixin` tillsammans. Vi registrerade en användares intresse för en viss författare; säg nu att vi vill låta dem lämna ett meddelande där de säger varför de gillar dem. Återigen, låt oss anta att vi inte kommer att lagra detta i en relationsdatabas utan istället i något mer esoteriskt som vi inte kommer att oroa oss för här.

Vid denna tidpunkt är det naturligt att nå en `Form` för att kapsla in den information som skickas från användarens webbläsare till Django. Säg också att vi är starkt investerade i REST, så vi vill använda samma URL för att visa författaren som för att fånga meddelandet från användaren. Låt oss skriva om vår `AuthorDetailView` för att göra det.

Vi behåller `GET`-hanteringen från `DetailView`, även om vi måste lägga till en `Form` i kontextdata så att vi kan rendera den i mallen. Vi kommer också att vilja dra in formulärbehandling från `FormMixin`, och skriva lite kod så att formuläret på `POST` anropas på lämpligt sätt.

Observera

Vi använder `FormMixin` och implementerar `post()` själva istället för att försöka blanda `DetailView` med `FormView` (som redan tillhandahåller en lämplig `post()`) eftersom båda vyerna implementerar `get()`, och saker och ting skulle bli mycket mer förvirrande.

Vår nya `AuthorDetailView` ser ut så här:

    # CAUTION: you almost certainly do not want to do this.
    # It is provided as part of a discussion of problems you can
    # run into when combining different generic class-based view
    # functionality that is not designed to be used together.

    from django import forms
    from django.http import HttpResponseForbidden
    from django.urls import reverse
    from django.views.generic import DetailView
    from django.views.generic.edit import FormMixin
    from books.models import Author

    class AuthorInterestForm(forms.Form):
        message = forms.CharField()

    class AuthorDetailView(FormMixin, DetailView):
        model = Author
        form_class = AuthorInterestForm

        def get_success_url(self):
            return reverse("author-detail", kwargs={"pk": self.object.pk})

        def post(self, request, *args, **kwargs):
            if not request.user.is_authenticated:
                return HttpResponseForbidden()
            self.object = self.get_object()
            form = self.get_form()
            if form.is_valid():
                return self.form_valid(form)
            else:
                return self.form_invalid(form)

        def form_valid(self, form):
            # Here, we would record the user's interest using the message
            # passed in form.cleaned_data['message']
            return super().form_valid(form)

`get_success_url()` ger någonstans att omdirigera till, som används i standardimplementeringen av `form_valid()`. Vi måste tillhandahålla vår egen `post()` som tidigare nämnts.

### En bättre lösning

Antalet subtila interaktioner mellan `FormMixin` och `DetailView` testar redan vår förmåga att hantera saker. Det är osannolikt att du skulle vilja skriva den här typen av klass själv.

I det här fallet kan du skriva `post()`-metoden själv och behålla `DetailView` som den enda generiska funktionaliteten, även om det innebär mycket dubbelarbete att skriva `Form`-hanteringskod.

Alternativt skulle det fortfarande vara mindre arbete än ovanstående tillvägagångssätt att ha en separat vy för bearbetning av formuläret, som kan använda `FormView` skilt från `DetailView` utan problem.

### En alternativ bättre lösning

Vad vi egentligen försöker göra här är att använda två olika klassbaserade vyer från samma URL. Så varför inte göra just det? Vi har en mycket tydlig uppdelning här: `GET` förfrågningar bör få `DetailView` (med `Form` läggs till i kontextdata), och `POST` förfrågningar bör få `FormView`. Låt oss ställa in dessa vyer först.

Vyn `AuthorDetailView` är nästan densamma som när vi först introducerade AuthorDetailView; vi måste skriva vår egen `get_context_data()` för att göra `AuthorInterestForm` tillgänglig för mallen. Vi hoppar över `get_object()` överskrivningen från tidigare för tydlighetens skull:

    from django import forms
    from django.views.generic import DetailView
    from books.models import Author

    class AuthorInterestForm(forms.Form):
        message = forms.CharField()

    class AuthorDetailView(DetailView):
        model = Author

        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            context["form"] = AuthorInterestForm()
            return context

Då är `AuthorInterestFormView` en `FormView`, men vi måste ta in `SingleObjectMixin` så att vi kan hitta den författare vi pratar om, och vi måste komma ihåg att ställa in `template_name` för att säkerställa att formulärfel kommer att göra samma mall som `AuthorDetailView` använder på `GET`:

    from django.http import HttpResponseForbidden
    from django.urls import reverse
    from django.views.generic import FormView
    from django.views.generic.detail import SingleObjectMixin

    class AuthorInterestFormView(SingleObjectMixin, FormView):
        template_name = "books/author_detail.html"
        form_class = AuthorInterestForm
        model = Author

        def post(self, request, *args, **kwargs):
            if not request.user.is_authenticated:
                return HttpResponseForbidden()
            self.object = self.get_object()
            return super().post(request, *args, **kwargs)

        def get_success_url(self):
            return reverse("author-detail", kwargs={"pk": self.object.pk})

Finally we bring this together in a new `AuthorView` view. We already know that calling `as_view()` on a class-based view gives us something that behaves exactly like a function based view, so we can do that at the point we choose between the two subviews.

You can pass through keyword arguments to `as_view()` in the same way you would in your URLconf, such as if you wanted the `AuthorInterestFormView` behavior to also appear at another URL but using a different template:

    from django.views import View

    class AuthorView(View):
        def get(self, request, *args, **kwargs):
            view = AuthorDetailView.as_view()
            return view(request, *args, **kwargs)

        def post(self, request, *args, **kwargs):
            view = AuthorInterestFormView.as_view()
            return view(request, *args, **kwargs)

Detta tillvägagångssätt kan också användas med alla andra generiska klassbaserade vyer eller dina egna klassbaserade vyer som ärver direkt från `View` eller `TemplateView`, eftersom det håller de olika vyerna så separata som möjligt.

## Mer än bara HTML

Klassbaserade vyer är perfekta när du vill göra samma sak många gånger. Anta att du skriver ett API och att varje vy ska returnera JSON istället för renderad HTML.

Vi kan skapa en mixin-klass som kan användas i alla våra vyer och hantera konverteringen till JSON en gång.

Till exempel kan en JSON-mixin se ut ungefär så här:

    from django.http import JsonResponse

    class JSONResponseMixin:
        """
        A mixin that can be used to render a JSON response.
        """

        def render_to_json_response(self, context, **response_kwargs):
            """
            Returns a JSON response, transforming 'context' to make the payload.
            """
            return JsonResponse(self.get_data(context), **response_kwargs)

        def get_data(self, context):
            """
            Returns an object that will be serialized as JSON by json.dumps().
            """
            # Note: This is *EXTREMELY* naive; in reality, you'll need
            # to do much more complex handling to ensure that arbitrary
            # objects -- such as Django model instances or querysets
            # -- can be serialized as JSON.
            return context

Observera

Kolla in Serialisering av Django-objekt-dokumentationen för mer information om hur man korrekt omvandlar Django-modeller och querysets till JSON.

This mixin provides a `render_to_json_response()` method with the same signature as `render_to_response()`. To use it, we need to mix it into a `TemplateView` for example, and override `render_to_response()` to call `render_to_json_response()` instead:

    from django.views.generic import TemplateView

    class JSONView(JSONResponseMixin, TemplateView):
        def render_to_response(self, context, **response_kwargs):
            return self.render_to_json_response(context, **response_kwargs)

På samma sätt kan vi använda vår mixin med en av de generiska vyerna. Vi kan göra vår egen version av `DetailView` genom att blanda `JSONResponseMixin` med `BaseDetailView` – (`DetailView` innan mallrenderingsbeteendet har blandats in):

    from django.views.generic.detail import BaseDetailView

    class JSONDetailView(JSONResponseMixin, BaseDetailView):
        def render_to_response(self, context, **response_kwargs):
            return self.render_to_json_response(context, **response_kwargs)

Denna vy kan sedan distribueras på samma sätt som alla andra `DetailView`, med exakt samma beteende - förutom svarets format.

If you want to be really adventurous, you could even mix a `DetailView` subclass that is able to return both HTML and JSON content, depending on some property of the HTTP request, such as a query argument or an HTTP header. Mix in both the `JSONResponseMixin` and a `SingleObjectTemplateResponseMixin`, and override the implementation of `render_to_response()` to defer to the appropriate rendering method depending on the type of response that the user requested:

    from django.views.generic.detail import SingleObjectTemplateResponseMixin

    class HybridDetailView(
        JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView
    ):
        def render_to_response(self, context):
            # Look for a 'format=json' GET argument
            if self.request.GET.get("format") == "json":
                return self.render_to_json_response(context)
            else:
                return super().render_to_response(context)

Because of the way that Python resolves method overloading, the call to `super().render_to_response(context)` ends up calling the `render_to_response()` implementation of `TemplateResponseMixin`.