🌊

DjangoのORMで子テーブルのカラムを使ってfilterする

2023/02/27に公開

書くこと

  • distinctを使ってフィルターする方法
  • annotateを使ってフィルターする方法

利用技術

  • Django

想定

モデル

以下のArticleとCommentモデルがあるとする。

class Article(models.Model):
    title = models.CharField(max_length=100)
    # (省略)


class Comment(models.Model):
    text = models.CharField(max_length=100)
    active = models.BooleanField(default=True)
    # Article 1-* Comment
    article = models.ForeignKey(
        "Article",
        models.PROTECT,
        related_name="comments",
    )
    # (省略)

シチュエーション

1件以上のactiveなCommentと紐づくArticleを取得したい

方法論

distinctを使ってフィルターする方法

  1. 子テーブル__columnsでフィルターする
  2. left outer joinしているため、distinct()する
return Article.objects.filter(
    comments__active=True
).distinct()

left outer joinをするため、distinct()をすることで期待通りの値を取得可能

annotateを使ってフィルターする方法

  1. articleにannotateを使ってBool型のhas_commentsカラムを付与する
  2. has_commentsでfilterする

article = Article.objects.annotate(
    comment_count=Count(
        "comments",
        filter=(comments__active=True)
    )
).annotate(
    has_comments=Case(
        When(comment_count__gt=0, then=True),
        default=False,
        output_field=BooleanField()
    )
)

return article.filter(has_comments=True, )

Discussion