[Django]ManyToManyの有無でフィルタリングしたい
動機
Django(3.1)で、ManyToManyField(多対多のrelation)を持つmodelを考えます。
このとき、対象のFieldにレコードがセットされているか否かでフィルタリングをしたい場合があります。
例えば以下のようなmodelを考えます。一つの記事に複数のタグがついている想定です。
models.py
from django.db import models
# タグ
class Tag(models.Model):
name=models.CharField(verbose_name="名前",max_length=32)
# 記事
class Article(models.Model):
title=models.CharField(verbose_name="タイトル",max_length=32)
text=models.TextField(verbose_name="本文")
tags=models.ManyToManyField(Tag,verbose_name="タグ",related_name="articles")
ここで、タグが一つでもついている記事の一覧を引っ張りたいとします。まず思いつくのは以下の書き方です。
views.py
from django.views.generic import ListView
from .models import *
class ArticleWithTagsListView(ListView):
model=Article
template_name="article_with_tags.html"
def get_queryset(self):
# tags__isnull=Falseで、タグが少なくとも一個ついているレコードだけ引っ張る
queryset=Article.objects.filter(tags__isnull=False)
return queryset
しかしこの方法はうまくいきません。紐づいているTag数に応じてレコードが重複してしまいます。
かといって
views.py
queryset=Article.objects.all()
filtered_queryset=[q for q in queryset if q.tags.count()>0]
なんてしようものならループの度にクエリが走って、実行時間がえらいことになります。
distinct()を使おう
最後にdistinct()を絡めれば、isnullフィルターを有効活用できます。
views.py
queryset=Article.objects.filter(tags__isnull=False).distinct()
annotate()を使おう
annotate()を使って各レコードのtag数を算出→countが0より大きいものをフィルタリングという方法をとってもOKです。
views.py
from django.db.models import Count
queryset=Article.objects.annotate(tag_count=Count('tag')).filter(tag_count__gt=0)
分解すると、まず下記の操作で"tag_count"という値に各レコードのtagの数を格納しています。
views.py
queryset=Article.objects.annotate(tag_count=Count('tag'))
#例 : 最初のレコードのtag数が3のとき
q=queryset.first()
print(q.tag_count) # 3
次に、"tag_count"が0より大きいレコードだけを抽出しています。
views.py
queryset=Article.objects.annotate(tag_count=Count('tag'))
queryset=queryset.filter(tag_count__gt=0) #追加
Author And Source
この問題について([Django]ManyToManyの有無でフィルタリングしたい), 我々は、より多くの情報をここで見つけました https://qiita.com/rokko2massy/items/0f6635420aa7675de533著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .