2017-04-24 29 views
0

我有一個用例,我必須計算ManyToManyField的出現次數,但它比我想象的要複雜得多。ManytoManyField的反向計數條件

models.py:他們的整體,但只對那些People誰擁有> 0和< 6標籤出現

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

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

在這裏,我不得不拿出Tags列表的數量和時間。喜歡的東西:

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

這是我想出了最初的計數:

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模型多次迭代(記錄在人中比在標籤中的人大)。

直覺上我認爲我應該能夠做一個Tag模型的迭代而不需要任何迭代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

花了很多時間在這個,但無法弄清楚爲什麼上面的第二種方法不起作用。任何人? – 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

抱歉,延遲退回。我繼續前進,並沒有回到這個問題,而是一個簡單的問題:你認爲即使'tag'是'People'模型中的'ManyToManyField',表達式'Tag.objects.filter(people ...)而不是反之亦然?因爲我認爲我們必須使用'_set'來進行查詢 – Anupam

+0

不用擔心,可能對別人有幫助。是的,這是有效的,這是django ORM如何爲ManyToMany字段提供反向關係的參考。 – kaushal