Having the following models:
class Post(models.Model):
title = models.CharField(max_length=250)
tags = TaggableManager()
and the data are:
**post.title** **post.tags**
Django By Example python,django,web
Who was Django Reinhardt python,django,
Test-Driven Development with Python python,web
Python for Data Analysis python,data
Learning Python python
Programming Python python
Automate the Boring Stuff with Python python
I try to code below
>>> alist=Post.objects.filter(tags__name__in=["data","python"])
>>> for i in alist.annotate(sam_tags=Count('tags')):
... print("\n---",i)
... print("tags of it : ",i.tags.all())
... print("value of sam_tags : ",i.sam_tags)
...
--- Django By Example
tags of it : [<Tag: django>, <Tag: python>, <Tag: web>]
value of sam_tags : 1
--- Who was Django Reinhardt
tags of it : [<Tag: django>, <Tag: python>]
value of sam_tags : 1
--- Automate the Boring Stuff with Python
tags of it : [<Tag: python>]
value of sam_tags : 1
--- Test-Driven Development with Python
tags of it : [<Tag: python>, <Tag: web>]
value of sam_tags : 1
--- Learning Python
tags of it : [<Tag: python>]
value of sam_tags : 1
--- Python for Data Analysis
tags of it : [<Tag: data>, <Tag: python>]
value of sam_tags : 2
--- Programming Python
tags of it : [<Tag: python>]
value of sam_tags : 1
>>>
why is the value of slist[0].sam_tags (post:Django By Example) equal to 1?
I think the post object of (post:Django By Example) has three tags [python,django and web] after reading the Django's documentation.
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#count
It said that Count(expression) returns the number of objects that are related through the provided expression. so the code
>>>alist[0].tags.all()
[<Tag: django>, <Tag: python>, <Tag: web>]
shows there are three tags in alist[0].tags,
>>> slist=alist.annotate(sam_tags=Count('tags'))
>>> slist[0].tags.all()
[<Tag: django>, <Tag: python>, <Tag: web>]
>>> slist[0].sam_tags
1
but here I get the value 1,
why?
I understand that Django is counting only the "python" and "data" tags which I included in my filter clause - other tags are not counted.
The output of "slist[0].tags.all()" shows that slist[0] has three tags related with itself. Because the documentation of django says that Count(expression) returns the number of objects that are related through the provided expression, slist[0].sam_tags should be 3 according to the documentation, but django-taggit make slist[0].sam_tags to be 1.
So what I really want to know is how django-taggit lets Count(expression) in the filter clause only calculate the number of tags in the filter condition .
Django is counting only the python
and data
tags which you included in your filter clause - other tags are not counted. (Note that the only example with sam_tags
of 2 is the one tagged both data
and python
.) This is probably unexpected behavior, but makes sense if you consider how the underlying SQL is executed. See this example from a similar schema to yours:
>>> a = Article.objects.filter(tags__slug__in=['python']).annotate(num_tags=Count('tags'))[0]
>>> a.num_tags
1
>>> a.tags.count()
2
If I change the filter clause to filter on somethign other than tags, it behaves as expected:
>>> Article.objects.filter(pk=a.pk).annotate(num_tags=Count('tags'))[0].num_tags
2
>>> Article.objects.filter(pk=a.pk).annotate(num_tags=Count('tags'))[0].tags.count()
2
it's relatively straight forward to look into the query by print(similar_post.query) we can see the code only counts the tag_id which belongs to the tags of the post is being shown.
SELECT "blog_post"."id", "blog_post"."title", "blog_post"."slug", "blog_post"."author_id", "blog_post"."body", "blog_post"."publish", "blog_post"."created", "blog_post"."updated", "blog_post"."status", COUNT("taggit_taggeditem"."tag_id") AS "same_tags" FROM "blog_post"
INNER JOIN "taggit_taggeditem" ON ("blog_post"."id" = "taggit_taggeditem"."object_id" AND ("taggit_taggeditem"."content_type_id" = 7))
WHERE ("blog_post"."status" = published
AND "taggit_taggeditem"."tag_id" IN (SELECT DISTINCT U0."id" FROM "taggit_tag"
U0 INNER JOIN "taggit_taggeditem" U1 ON (U0."id" = U1."tag_id")
INNER JOIN "django_content_type" U2 ON (U1."content_type_id" = U2."id")
WHERE (U2."app_label" = blog AND U2."model" = post AND U1."object_id" = 8))
AND NOT ("blog_post"."id" = 8))
GROUP BY "blog_post"."id", "blog_post"."title", "blog_post"."slug", "blog_post"."author_id", "blog_post"."body", "blog_post"."publish", "blog_post"."created", "blog_post"."updated", "blog_post"."status"
ORDER BY "same_tags" DESC, "blog_post"."publish" DESC LIMIT 4
来源:https://stackoverflow.com/questions/39574909/filter-tags-of-django-taggit-in-djangos-queryset