# Django 6.0.7 ドキュメント

# クラスベースのビューでミックスイン (mixin) を使用する

注意

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

Django のビルトインのクラスベースビューではたくさんの機能が準備されていますが、個別に使いたい機能もあるかもしれません。たとえば、HTTP レスポンスを生成するテンプレートをレンダリングするビューを記述したいとき、`TemplateView` は使えない状況もあります; `POST` ではテンプレートをレンダリングするだけで、`GET` のときはまったく異なる処理がしたいときなどです。この場合、`TemplateResponse` を直接使えますが、コードが重複する結果となってしまいます。

この理由から、Django は個別の機能を提供する多くの mixin を用意しています。たとえば、テンプレートのレンダリングは `TemplateResponseMixin` でカプセル化されています。Django のリファレンスドキュメントには すべてのミックスインの完全なドキュメント があります。

## コンテキストとテンプレートのレスポンス

2 つの中心的なミックスイン (mixin) が用意されており、クラスベースビュー内のテンプレートを扱うインターフェースに一貫性を保ちやすくなっています。

`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()` 自体は `get_template_names()` を呼び出しますが、デフォルトではクラスベースのビューで `template_name` を検索します。他の2つのミックスイン (`SingleObjectTemplateResponseMixin` と `MultipleObjectTemplateResponseMixin`) は、実際のオブジェクトを扱うときに、より柔軟なデフォルトを提供するためにこれをオーバーライドします。

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

## Django の一般的なクラスベースのビューを構築する

Djangoのクラスベースのジェネリックビューが、個々の機能を提供するミックスインからどのように構築されているか見てみましょう。オブジェクトの「詳細」ビューをレンダリングする `DetailView` と、クエリセットから典型的にオブジェクトのリストをレンダリングし、オプションでページ分割する `ListView` を考えます。これにより、単一のDjangoオブジェクトや複数のオブジェクトを扱う際に便利な機能を提供する4つのミックスインについて紹介します。

ジェネリック編集ビュー (`FormView` と、モデル固有のビュー `CreateView`, `UpdateView`, `DeleteView`) と日付ベースのジェネリックビューにもミックスインがあります。これらは ミックスインのリファレンス で紹介されています。

### `DetailView`: Django の1つのオブジェクトを対象とするビュー

オブジェクトの詳細を表示するためには、基本的に2つの作業が必要です。まずはオブジェクトを検索し、それから適切なテンプレートと、そのオブジェクトをコンテキストとして、 `TemplateResponse` を作成します。

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`: Django の複数のオブジェクトを対象とするビュー

オブジェクトのリストもおおよそ同じパターンに従います。オブジェクトの (おそらくページ分割された) リスト、典型的には `QuerySet` が必要で、次にそのオブジェクトのリストを使って、適切なテンプレートで `TemplateResponse` を作る必要があります。

オブジェクトを取得するために、 `ListView` は `MultipleObjectMixin` を使用します。このミックスインは、 `get_queryset()` と `paginate_queryset()` の両方を提供します。 `SingleObjectMixin` とは異なり、URL の一部をキーにしてクエリセットを特定する必要はありません。そのため、デフォルトではビュークラスの `queryset` または `model` 属性が使用されます。ここで `get_queryset()` をオーバーライドする一般的な理由は、現在のユーザーに依存するなど、オブジェクトを動的に変化させることで、ブログの場合は将来の投稿を除外することなどが理由となるでしょう。

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

To make a `TemplateResponse`, `ListView` then uses `MultipleObjectTemplateResponseMixin`. As with `SingleObjectTemplateResponseMixin` above, this overrides `get_template_names()` to provide a range of options, with the most commonly-used being `<app_label>/<model_name>_list.html`, with the `_list` part again being taken from the `template_name_suffix` attribute. (The date based generic views use suffixes such as `_archive`, `_archive_year` and so on to use different templates for the various specialized date-based list views.)

## Django のクラスベースのビューのミックスイン (mixin) を使用する

Django のクラスベースのジェネリックビューが、提供されたミックスインをどのように使うか見てきたので、それらを組み合わせる他の方法を見てみましょう。組み込みのクラスベースのビューや、他のクラスベースのジェネリックビューと組み合わせることに変わりはありませんが、Django を「箱から出してすぐに」使えるものよりも、もっとレアな問題をカバーしています。

警告

すべてのミックスインを一緒に使用することができるわけではなく、すべてのクラスベースのジェネリックビューをすべての他のミックスインと一緒に使用することもできません。ここではいくつかの動作する例を紹介します。他の機能を組み合わせる場合は、使用するさまざまなクラス間で重複する属性やメソッドの相互作用、および method resolution order （メソッド解決順序: MRO）が、どのバージョンのメソッドがどんな順番で呼び出されるかに影響することを考慮する必要があります。

Django の クラスベースビュー および クラスベースビュー ミックスイン のリファレンスドキュメントは、異なるクラスやミックスイン間でよく競合を引き起こす属性やメソッドを理解するのに役立ちます。

もし迷ったら、 `View` や `TemplateView` をベースにして、 `SingleObjectMixin` や `MultipleObjectMixin` を使うのが良いでしょう。おそらく、より多くのコードを書くことになるでしょうが、後でそのコードに辿り着いた他の誰かが明確に理解できる可能性が高くなります。(もちろん、Django のクラスベースのジェネリックビューの実装を見て、問題への取り組み方のヒントを得ることもできます)。

### ビューで `SingleObjectMixin` を使用する

もし `POST` にのみ反応するクラスベースのビューを書きたいのであれば、 `View` をサブクラス化し、その中に `post()` メソッドを書きます。しかし、URLから特定したオブジェクトに対して処理を行いたい場合、 `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})
            )

実際には、リレーショナルデータベースではなく、キーバリューストアに関心事を記録したいと思うでしょう。その部分は省略しました。 `SingleObjectMixin` を使用するビューで興味があるのは作者を調べるところだけで、これは `self.get_object()` を呼び出すことで行います。それ以外は全てミックスインが行ってくれます。

これをURLにフックするのは簡単です:

`urls.py`

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

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

`pk` という名前のグループに注目してください。 `get_object()` は `Author` のインスタンスを検索するために使用します。スラグを使うこともできますし、 `SingleObjectMixin` の他の機能を使うこともできます。

### `ListView` で `SingleObjectMixin` を使用する

`ListView` は組み込みのページ分割を提供しますが、別のオブジェクトに(外部キーで)リンクされたオブジェクトのリストをページ分割したいかもしれません。出版の例では、特定の出版社のすべての本をページ分割したいかもしれません。

これを行う1つの方法は、 `ListView` と `SingleObjectMixin` を組み合わせることで、ページ分割された本のリストのクエリセットを、見つかった出版社から単一のオブジェクトとしてぶら下げることができます。これを行うには、2つの異なるクエリセットを用意する必要があります:

`ListView` で使用するための `Book` の クエリセット

  リストアップしたい本の `Publisher` にアクセスできるので、 `get_queryset()` をオーバーライドして、 `Publisher` の 逆方向の外部キーマネージャ を使用します。

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

  正しい `Publisher` オブジェクトを取得するには、デフォルトの `get_object()` の実装に頼りましょう。ただし、明示的に `queryset` 引数を渡さなければなりません。なぜなら、デフォルトの `get_object()` の実装では `get_queryset()` を呼び出しますが、これは `Publisher` の代わりに `Book` オブジェクトを返すようにオーバーライドしているからです。

注釈

`get_context_data()` については慎重に考える必要があります。 `context_object_name` がセットされている場合、 `SingleObjectMixin` と `ListView` の両方がその値のもとのコンテキストデータに格納されます。そのため、代わりに `Publisher` が明示的にコンテキストデータに含まれるようにする必要があります。 `ListView` は `super()` を呼び出すことを忘れなければ、適切な `page_obj` と `paginator` を自動で追加してくれます。

これで新しい `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()

`get()` 内で `self.object` を設定していることに注目してください。これにより、後で `get_context_data()` や `get_queryset()` で再利用できます。 `template_name` を指定しない場合、テンプレートは通常の `ListView` の選択肢にデフォルトで設定されます。この場合、それは `"books/book_list.html"` になります。なぜなら、これは本のリストだからです。 `ListView` は `SingleObjectMixin` について何も知らないため、このビューが `Publisher` と関連していることを全く理解していません。

この例では `paginate_by` を意図的に小さくしているので、ページ分割の動作を確認するためにたくさんのブックを作成する必要はありません！以下が使いたいテンプレートです:

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

## これより複雑なことはやめておきましょう

一般的に必要な機能は、 `TemplateResponseMixin` や `SingleObjectMixin` で実現できます。上記のように、少し注意すれば、`SingleObjectMixin` を `ListView` と組み合わせることもできます。しかし、そうするとますます複雑になります。適切な判断基準は以下の通りです:

ヒント

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

より洗練されたものにしようとするとどうなるかを示すために、より単純な解決策がある場合に可読性と保守性を犠牲にする例を示します。まず、 `DetailView` と `FormMixin` を組み合わせて、 `DetailView` を使ってオブジェクトを表示しているのと同じ URL に Django `Form` を `POST` できるようにしようという素朴な試みを見てみましょう。

### `DetailView` で `FormMixin` を使用する

先ほどの `View` と `SingleObjectMixin` を一緒に使う例を思い出してください。ユーザが特定の作者に興味を持ったことを記録していました。ここで、なぜその作者が好きなのか、メッセージを残させたいとします。ここではリレーショナルデータベースではなく、より難解な方法で情報を保存することになると仮定しましょう。その詳細についてはここでは考慮しません。

この時点で、ユーザのブラウザから Django に送られる情報をカプセル化する `Form` に手を伸ばすのは自然なことです。また、私たちは REST に重きを置いているので、ユーザからのメッセージを取得するのと同じように、作者を表示するのにも同じ URL を使いたいとします。そのために `AuthorDetailView` を書き換えてみましょう。

`GET` の処理は `DetailView` から引き継ぎますが、コンテキストデータに `Form` を追加して、テンプレートでレンダリングできるようにします。また、 `FormMixin` からフォーム処理を取り込みます。そして、 `POST` 時にフォームが適切に呼び出されるようにコードを少し書きましょう。

注釈

`FormMixin` を使用し、自分で `post()` を実装します。 `FormView` （すでに適切な `post()` を提供している）と `DetailView` は、両方のビューが `get()` を実装しているため、混在させるとより混乱する可能性があるからです。

新しい `AuthorDetailView` は次のようになります:

    # 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()` はリダイレクト先を提供し、デフォルト実装の `form_valid()` で使用されます。前述のように、独自の `post()` を用意する必要があります。

### より良い解決策

`FormMixin` と `DetailView` の間の微妙な相互作用の数は、すでに私たちの管理能力を試しています。このようなクラスを自分で書こうとは思わないでしょう。

この場合、 `post()` メソッドを自分で書くことはできますが、 `DetailView` を唯一の汎用機能として保持し、 `Form` を処理するコードを自分で書くことは多くの重複を伴います。

代わりに、フォームを処理するための別々のビューを持つ方が、上記のアプローチよりも作業量が少ないでしょう。その場合、 `DetailView` とは別の `FormView` を使うことになるでしょう。

### もう一つのより良い解決策

ここで本当にやろうとしていることは、同じURLから2つの異なるクラスベースのビューを使うことです。では、なぜそうしないのでしょうか？ここでは非常に明確な区分があります。 `GET` リクエストは `DetailView` (コンテキストデータに `Form` を追加したもの) を取得し、 `POST` リクエストは `FormView` を取得します。まずはこれらのビューを設定しましょう。

`AuthorDetailView` ビューは、最初に紹介した AuthorDetailView とほとんど同じです。 `AuthorInterestForm` をテンプレートで利用できるようにするために、独自の `get_context_data()` を書く必要があります。わかりやすくするために、先ほどの `get_object()` のオーバーライドは省略します:

    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

次に `AuthorInterestFormView` は `FormView` ですが、 `SingleObjectMixin` を持ってきて、今話題にしている著者を見つけられるようにしなければなりません。また、 `AuthorDetailView` が `GET` で使用しているのと同じテンプレートをフォームエラーでレンダリングできるように `template_name` を忘れずに指定しなければなりません:

    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)

この方法は、他のクラスベースのジェネリックビューや、 `View` や `TemplateView` を直接継承した独自のクラスベースのビューでも使用できます。異なるビューを可能な限り分離した状態に保つことができるからです。

## 単純な HTML を超える

クラスベースビューが役に立つのは、同じことを何度もしたい場合です。たとえば API を作成している場合、全てのビューはレンダリングされた HTML ではなく JSON を返す必要があります。

全てのビューで利用するために、 JSONへの変換処理をするmixinクラスを作成できます。

たとえば、JSONのmixinは以下のようになるでしょう。

    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

注釈

Djangoモデルやクエリセットを正しくJSONに変換する方法についての詳細は Django オブジェクトのシリアライズ ドキュメントを参照してください。

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)

同様に、このミックスインをジェネリックビューで使用することもできます。 `JSONResponseMixin` と `BaseDetailView` (テンプレートレンダリングの動作が混ざる前の `DetailView`) をミックスインすることで、 `DetailView` の独自のバージョンを作ることができます:

    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)

このビューはレスポンスの作成を除いて、他の `DetailView` と同じように記述され、同じように動作します。

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