# Dokumentasi Django 5.2.8

# Menggunakan mixin dengan tampilan berdasarkan-kelas

Hati-hati

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

Tampilan berdasarkan-kelas siap-pakai Django menyediakan banyak fungsionalitas, tetapi beberapa dari itu anda mungkin ingin menggunakannya terpisah. Sebagai contoh, anda mungkin ingin menulis sebuah tampilan yang membangun cetakan untuk membuat tanggapan HTTP, tetapi anda tidak dapat menggunakan `TemplateView`; mungkin anda butuh membangun cetakan hanya pada `POST`, dengan `GET` melakukan sesuatu lain seluruhnya. Selagi anda dapat menggunakan `TemplateResponse` langsung, ini akan kemungkinan hasil di kode ganda.

Untuk alasan ini, Django juga menyediakan sejumlah mixin yang menyediakan fungsionalitas diskrit lebih. Pembangunan cetakan, sebagai contoh, dienkapsulasi di `TemplateResponseMixin`. Dokumentasi acuan Django mengandung full documentation of all the mixins.

## Tanggapan koteks dan cetakan

Dua mixin pusat disediakan yang membantu dalam antarmuka tetap untuk bekerja dengan cetakan di tampilan berdasarkan-kelas.

`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()` itself calls `get_template_names()`, which by default will look up `template_name` on the class-based view; two other mixins (`SingleObjectTemplateResponseMixin` and `MultipleObjectTemplateResponseMixin`) override this to provide more flexible defaults when dealing with actual objects.

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

## Membangun tampilan berdasarkan-kelas umum Django

Mari kita lihat bagaimana tampilan berdasarkan-kelas umum Django adalah membangun dari mixin disediakan fungsionalitas diskrit. Kami akan mempertimbangkan `DetailView`, yang membangun tampilan "detail" dari sebuah obyek, dan `ListView`, yang akan membangun sebuah daftar dari obyek, khususnya dari queryset, dan pilihannya memberi nomor mereka. Ini akan memperkenalkan kita untuk empat mixin yang diantara mereka menyediakan fungsionalitas berguna dengan antara obyek Django tunggal, atau banyak obyek.

Ada juga mixin terlibat di tampilan sunting umum (`FormView`, dan tampilan model-tertentu `CreateView`, `UpdateView` dan `DeleteView`), dan di tampilan umum berdasarkan-tanggal. Ini dicakupi di mixin reference documentation.

### `DetailView`: bekerja dengan obyek tunggal Django

Untuk menunjukkan rincian dari sebuah obyek, kami dasarnya butuh melakukan dua hal: kami butuh mencari obyek dan kemudian kami butuh membuat `TemplateResponse` dengan cetakan yang cocok, dan obyek itu sebagai konteks.

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`: bekerja dengan banyak obyek Django

Daftar dari obyek mengikuti kurang lebih pola sama: kami butuh sebuah (kemungkinan diberikan nomor) daftar obyek, khususnya sebuah `QuerySet`, dan kemudian kami butuh membuat sebuah `TemplateResponse` dengan cetakan cocok menggunakan daftar itu dari obyek.

Untuk mendapatkan objek, `ListView` menggunakan `MultipleObjectMixin`, yang menyediakan `get_queryset()` dan `paginate_queryset()`. Berbeda dengan `SingleObjectMixin`, tidak perlu untuk mengunci bagian dari URL untuk mengetahui kumpulan permintaan yang akan digunakan, jadi awalan menggunakan `queryset` atau `model` pada kelas tampilan. Alasan umum untuk mengesampingkan `get_queryset()` di sini adalah untuk memvariasikan objek secara dinamis, seperti bergantung pada pengguna saat ini atau untuk mengecualikan kiriman di masa mendatang untuk blog.

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

Untuk membuat sebuah `TemplateResponse`, `ListView` kemudian menggunakan `MultipleObjectTemplateResponseMixin`; seperti `SingleObjectTemplateResponseMixin` diatas, ini menimpa `get_template_names()` untuk menyediakan `a range of options`, yang paling digunakan-umum berwujud `<app_label>/<model_name>_list.html`, dengan bagian `_list` kembali diambil dari atribut `template_name_suffix`. (Tampilan umum berdasarkan tanggal menggunakan akhiran seperti `_archive`, `_archive_year` dan sebagainya untuk menggunakan cetakan berbeda untuk beragam tampilan daftar berdasarkan-tanggal khusus.)

## Menggunakan mixin tampilan berdasarkan-kelas Django

Now we've seen how Django's generic class-based views use the provided mixins, let's look at other ways we can combine them. We're still going to be combining them with either built-in class-based views, or other generic class-based views, but there are a range of rarer problems you can solve than are provided for by Django out of the box.

Peringatan

Tidak semua mixin dapat digunakan bersama-sama, dan tidak semua tampilan berdasarkan kelas umum dapat digunakan dengan semua mixin lain. Disini kami menghadirkan beberapa contoh yang melakukan pekerjaan; jika anda ingin membawa bersama-sama fungsionalitas lain kemudian anda akan harus mempertimbangkan interaksi diantara atribut dan metode yang tumpang tindih diantara kelas-kelas berbeda anda sedang gunakan, dan bagaimana method resolution order akan mempengaruhi versi mana dari metode akan dipanggil di urutan apa.

Acuan dokumentasi untuk class-based views dan class-based view mixins Django akan membantu anda dalam memahami atribut dan metode mana mungkin menyebabkan pertentangan diantara kelas dan mixin berbeda.

Jika ragu, itu sering lebih baik mundur dan dasarkan pekerjaan anda pada `View` atau `TemplateView`, mungkin dengan `SingleObjectMixin` dan `MultipleObjectMixin`. Meskipun anda akan mungkin mengakhiri menulis kode lebih, itu lebih mungkin jelas dapat dimengerti pada seseorang lain datang ke itu kemudian, dan dengan sedikit interaksi untuk khawatir tentang anda akan menyimpan beberapa pemikiran anda sendiri. (Tentu saja, anda dapat selalu mempelajari kedalam penerapan Django dari tampilan berdasarkan-kelas umum untuk ilham pada bagaimana memecahkan masalah.)

### Menggunakan `SingleObjectMixin` dengan View

If we want to write a class-based view that responds only to `POST`, we'll subclass `View` and write a `post()` method in the subclass. However if we want our processing to work on a particular object, identified from the URL, we'll want the functionality provided by `SingleObjectMixin`.

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

`views.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})
            )

In practice you'd probably want to record the interest in a key-value store rather than in a relational database, so we've left that bit out. The only bit of the view that needs to worry about using `SingleObjectMixin` is where we want to look up the author we're interested in, which it does with a call to `self.get_object()`. Everything else is taken care of for us by the mixin.

Kami dapat menghubungkan kedalam URL kami dengan cukup mudah:

`urls.py`

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

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

Catat kelompok bernama `pk`, yang `get_object()` menggunakan untuk mencari instance `Author`. Anda dapat juga menggunakan sebuah keong, atau apapun dari fitur-fitur lain dari `SingleObjectMixin`.

### Menggunakan `SingleObjectMixin` dengan `ListView`

`ListView` menyediakan penomoran siap-pakai, tetapi anda mungkin ingin memberi nomor daftar dari obyek yang semua terkait (oleh foreign key) ke obyek lain. Di contoh penerbitan kami, anda mungkin ingin memberi nomor melalui semua buku-buku oleh penerbit tertentu.

Satu cara melakukan ini adalah memadukan `ListView` dengan `SingleObjectMixin`, sehingga queryset untuk memberi nomor halaman dari buku dapat menggantung penerbit ditemukan sebagai obyek tunggal. Untuk melakukan ini, kami butuh memiliki dua queryset berbeda:

Queryset `Book` untuk digunakan oleh `ListView`

  Since we have access to the `Publisher` whose books we want to list, we override `get_queryset()` and use the `Publisher`’s reverse foreign key manager.

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

  Kami akan bergantung pada penerapan awalan dari `get_object()` untuk mengambil obyek `Publisher` benar. Bagaimanapun, kami butuh jelas melewatkan sebuah argumen `queryset` karena jika tidak penerapan awalan dari `get_object()` akan memanggil `get_queryset()` yang kami telah timpa untuk mengembalikan obyek `Book` daripada `Publisher`.

Catatan

Kami harus berpikir hati-hati tentang `get_context_data()`. Sejak kedua `SingleObjectMixin` dan `ListView` akan menaruh hal-hal dalam data konteks dibawah nilai dari `context_object_name` jika itu disetel, kami akan jelas memastikan `Publisher` adalah dalam data konteks. `ListView` akan menambah dalam `page_obj` dan `paginator` cocok untuk kami menyediakan kami ingat memanggil `super()`.

Sekarang kami dapat menulis `PublisherDetailView` baru:

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

Perhatikan bagaimana kami menyetel `self.object` dalam `get()` jadi kami dapat menggunakan itu kembali kemudian dalam `get_context_data()` dan `get_queryset()`. Jika anda tidak menyetel `template_name`, cetakan akan awalan menjadi pilihan `ListView` biasa, yang dalam kasus ini akan menjadi `"books/book_list.html"` karena itu adalah daftar dari buku-buku; `ListView` tidak mengetahui apapun tentang `SingleObjectMixin`, jadi itu tidak mempunyai petunjuk tampilan ini adalah hubungannya dengan `Publisher`.

`paginate_by` sengaja kecil dalam contoh sehingga anda tidak perlu membuat banyak buku untuk melihat penomoran halaman bekerja! Ini adalah cetakan anda ingin gunakan:

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

## Hindari apapun lebih rumit

Umumnya anda dapat gunakan `TemplateResponseMixin` dan `SingleObjectMixin` ketika anda butuh fungsionalitas mereka. Seperti ditunjukkan diatas, dengan sedikit perawatan anda dapat bahkan memadukan `SingleObjectMixin` dengan `ListView`. Bagaimanapun hal-hal meningkat semakin rumit ketika anda mencoba melakukannya, dan aturan bagus dari ibu jari adalah:

Petunjuk

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

Untuk menunjukkan apa yang terjadi ketika anda mencoba mendapatkan lebih mutakhir, kami menunjukkan sebuah contoh yang mengorbankan kesiapan dan rawatan ketika ada pemecahan termudah. Pertama, mari kita lihat usaha naif untuk memadukan `DetailView` dengan `FormMixin` untuk mengadakan kami pada `POST` sebuah `Form` Django ke URL sama ketika kami sedang memperlihatkan sebuah obyek menggunakan `DetailView`.

### Menggunakan `FormMixin` dengan `DetailView`

Berpikir kembali ke contoh paling awal dari menggunakan `View` dan `SingleObjectMixin` bersama-sama. Kami sedang merekam sebuah minat pengguna dalam penulis tertentu; katakan sekarang yang kami ingin membiarkan mereka meninggalkan pesan mengatakan mengapa mereka menyukainya. Kembali, mari kita anggap tidak menyimpan ini dalam basisdata hubungan tetapi sebagai gantinya di lebih esoterik yang kami tidak akan khawatir disini.

Pada titik ini itu adalah alamiah mencapai untuk `Form` untuk mengenkapsulasi informasi dikirim dari peramban pengguna pada Django. Katakan juga bahwa kami melakukan investasi di REST, jadi kami ingin menggunakan URL sama untuk memperlihatkan penulis sebagai untuk menangkap pesan dar pengguna. Mari kita menulis kembali `AuthorDetailView` kami untuk melakukan itu.

Kami akan menjaga penanganan `GET` dari `DetailView`, meskipun kami akan harus menambah `Form` kedalam data konteks jadi kami dapat mengirim itu dalam cetakan. Kami akan juga ingin menarik dalam pengolahan formulir `FormMixin`, dan menulis sedikit kode sehingga pada `POST` formulir mendapatkan panggilan yang sesuai.

Catatan

Kami menggunakan `FormMixin` dan menerapkan `post()` kami sendiri daripada mencoba mencampur `DetailView` dengan `FormView` (yang menyediakan sudah `post()` cocok) karena kedua dari tampilan menerapkan `get()`, dan hal-hal akan menjadi lebih membingungkan.

`AuthorDetailView` kami yang baru terlihat seperti ini:

    # 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()` provides somewhere to redirect to, which gets used in the default implementation of `form_valid()`. We have to provide our own `post()` as noted earlier.

### Pemecahan terbaik

The number of subtle interactions between `FormMixin` and `DetailView` is already testing our ability to manage things. It's unlikely you'd want to write this kind of class yourself.

In this case, you could write the `post()` method yourself, keeping `DetailView` as the only generic functionality, although writing `Form` handling code involves a lot of duplication.

Alternatively, it would still be less work than the above approach to have a separate view for processing the form, which could use `FormView` distinct from `DetailView` without concerns.

### Sebuah pilihan lain pemecahan terbaik

Apa kami sedang coba lakukan disini adalah menggunakan dua kelas bebeda berdasarkan tampilan dari URL sama. Jadi mengapa tidak lakukan hal itu? Kami mempunyai pembagian sangat jelas disini: permintaan `GET` harus mendapatkan `DetailView` (dengan `Form` ditambahkan ke data konteks), dan permintaan `POST` harus mendapatkan `FormView`. Mari kita menyetek tampilan tersebut dahulu.

The `AuthorDetailView` view is almost the same as when we first introduced AuthorDetailView; we have to write our own `get_context_data()` to make the `AuthorInterestForm` available to the template. We'll skip the `get_object()` override from before for clarity:

    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

Then the `AuthorInterestFormView` is a `FormView`, but we have to bring in `SingleObjectMixin` so we can find the author we're talking about, and we have to remember to set `template_name` to ensure that form errors will render the same template as `AuthorDetailView` is using on `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)

Pendekatan ini dapat juga digunakan dengan tampilan berdasarkan-kelas umum lainnya diwarisi langsung dari `View` or `TemplateView`, ketika itu menjaga tampilan berbeda sebagai terpisah mungkin.

## Lebih dari hanya HTML

Dimana bersinar tampilan berdasarkan-kelas adalah ketika anda ingin melakukan hal sama sebanyak kali. Kiranya anda sedang menulis sebuah API, dan setiap tampilan harus mengembalikan JSON daripada membangun HTML.

Kami dapat membuat sebuah kelas mixin untuk digunakan di semua dari tampilan kami, menangani perubahan pada JSON sekali.

Sebagai contoh, mixin JSON mungkin terlihat seperti ini:

    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

Catatan

Periksa dokumentasi Menserialkan obyek-obyek Django untuk informasi lebih pada bagaimana dengan benar merubah model Django dan queryset menjadi 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)

Equally we could use our mixin with one of the generic views. We can make our own version of `DetailView` by mixing `JSONResponseMixin` with the `BaseDetailView` -- (the `DetailView` before template rendering behavior has been mixed 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)

Tampilan ini dapat kemudian disebarkan dalam cara sama seperti lainnya `DetailView`, dengan persi perilaku sama -- kecuali untuk bentuk dari tanggapan.

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