2017-04-24 20 views
0

私はManyToManyFieldの発生回数をカウントする必要があるが、それは思ったよりも複雑になっています。条件付きのManytoManyFieldの逆数

models.py

ここ
class Tag(models.Model): 
    name = models.CharField(max_length=100, unique=True) 

class People(models.Model): 
    tag = models.ManyToManyField(Tag, blank=True) 

私はTagsのリストと、彼らはだけ> 0と< 6タグを持っているものをPeopleの全体的な表示された回数を思い付くする必要があります。以下のような何か:

q = People.objects.annotate(tag_count=Count('tag')).filter(tag_count__lte=6, tag_count__gt=0)  

for tag in Tag.objects.all(): 
    cnt = q.filter(tag__name=tag.name).count() 
    # doing something with the cnt 

しかし、私は後で私はおそらくPeopleモデルに何回を反復処理していますので、これは効率的ではないことに気づいた(レコード:

tag1 - 265338 
tag2 - 4649303 
tag3 - 36636 
... 

これは私が最初に、カウントを思い付いた方法です人はTagのものよりも大きくなります)。

私はTagモデルの1回の反復をPeopleモデルの繰り返しなしに実行できるはずです。そこで、私はこれを思いついた:

for tag in Tag.objects.all(): 
    cnt = tag.people_set.annotate(tag_count=Count('tag')).filter(tag_count__lte=6).count() 
    # doing something with the cnt 

しかし、これは期待された結果をもたらしません。第二に、私はこれがより複雑になっているように思えたので、おそらく私は簡単なことを複雑にしています。どんなアドバイスにもすべての耳。

更新:私はqueryset.queryを取得し、dbをデバッグするためにクエリを実行しました。何らかの理由で、結果の結合のtag_count列にすべての1が表示されます。理由を理解できないようだ。

+0

これで多くの時間を費やしましたが、上記の第2のアプローチがうまくいかない理由がわかりません。誰でも? – Anupam

答えて

1

逆のManyToManyフィールドクエリを使用して行うことができます。

オーバーヘッドを減らし、ほとんどのオーバーヘッドをPythonからデータベースサーバーに移行します。

from some_app.models import Tag, People 
from django.db.models import F, Value, Count, CharField 
from django.db.models.functions import Concat 

# queryset: people with tags >0 and <6, i.e. 1 to 5 tags 
people_qualified = People.objects.annotate(tag_count=Count('tag'))\ 
       .filter(tag_count__range=(1, 5)) 

# query tags used with above category of people, with count 
tag_usage = Tag.objects.filter(people__in=people_qualified)\ 
      .annotate(tag=F('name'), count=Count('people'))\ 
      .values('tag', 'count') 
# Result: <QuerySet [{'count': 3, 'tag': u'hello'}, {'count': 2, 'tag': u'world'}]> 

# similarily, if needed the string output 
tag_usage_list = Tag.objects.filter(people__in=people_qualified)\ 
       .annotate(tags=Concat(F('name'), Value(' - '), Count('people'), 
             output_field=CharField()))\ 
       .values_list('tags', flat=True) 
# Result: <QuerySet [u'hello - 3', u'world - 2']> 
+0

帰りが遅れて申し訳ありません。私は移動していませんでしたが、この質問に戻りませんでしたが、簡単な質問です。「タグ」が「People」モデルの「ManyToManyField」であっても、「Tag.objects.filter(people ...このようなクエリには '_set'を使わなければならないと思っていたので、 – Anupam

+0

他の人に助けになるかもしれませんが、これはうまくいきます。これはdjango ORMがManyToManyフィールドの逆関係を参照する方法です。 – kaushal

関連する問題