DjangoのForeignkeyでFilterをかける方法

しんぺい

こんにちは!しんぺいです。

DjangoのモデルではForeignkeyやManytoManyFieldを使うことが多いですが、今回は、ViewでForeignkeyを取得する際にFilterをかける方法を紹介します!

モデルを作成する

まずはモデルを作成します。

今回は、ModelAのnameフィールドと、ModelBのmodel_a_nameフィールドを紐づけることにしましょう。

models.py

from django.db import models

class ModelA(models.Model):
    name = models.CharField(max_length=255, verbose_name="名前")
    is_active = models.BooleanField(
        default=True, help_text='非表示の場合はFalseにする',
        verbose_name='ModelAの表示設定')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = "ModelA"
        verbose_name_plural = "ModelA"


class ModelB(models.Model):
    model_a_name = models.ForeignKey(
        ModelA, null=True, on_delete=models.PROTECT)
    title = models.CharField(max_length=255, blank=True,
                                null=True, default="", verbose_name="タイトル")
    is_active = models.BooleanField(
        default=True, help_text='非表示の場合はFalseにする',
        verbose_name='ModelBの表示設定')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "ModelB"
        verbose_name_plural = "ModelB"

ビューを作成する

テンプレートに渡すためのビューを作っていきましょう。

今回は、get_context_dataで「ModelAにぶらさがっているModelBを抽出する」ことにします。
まずは普通に引っ張り出してみます。

ForeignKeyのオブジェクトを引っ張り出すには、prefetch_relatedを使います。

views.py

from ..test.models import (
    ModelA,
    ModelB
)


class ResultListView(ListView):
    template_name = 'result/result_page.html'

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request)
        context['result'] = ModelA.objects.all().prefetch_related("modelb_set")
        return context

モデル名_setと書くことで、ぶらさがっているモデルのオブジェクトを引っ張ることが可能です。

次は、「ModelAで表示可能になっているもののみ取得し、かつForeignKeyも取得」としましょう。
単純にFilterをかければいいだけですね。

views.py

from ..test.models import (
    ModelA,
    ModelB
)


class ResultListView(ListView):
    template_name = 'result/result_page.html'

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request)
        context['result'] = ModelA.objects.filter(is_active=True).prefetch_related("modelb_set")
        return context

では、「ModelAで表示可能になっているもののみ取得し、かつForeignKeyも取得し、その中でも表示可能になっているもののみ」を取得することにしましょう。

これは少々厄介で、DjangoのPrefetchモデルを使う必要があります。

views.py

from django.db.models import Prefetch  #追加

from ..test.models import (
    ModelA,
    ModelB
)


class ResultListView(ListView):
    template_name = 'result/result_page.html'

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request)
        context['result'] = ModelA.objects.filter(is_active=True).prefetch_related(Prefetch("modelb_set", queryset=ModelB.objects.filter(is_active=True)))
        return context

これで希望通りのデータが取得できるようになりました!