Django ORM - 'annotate'和'order_by'似乎不等同于'GROUP BY'和'ORDER BY'?

3j86kqsm  于 2023-06-07  发布在  Go
关注(0)|答案(1)|浏览(155)

总结:

ordered_tags = tagged_items.annotate(freq=Count('tag_id')).order_by('-freq')
    for i in ordered_tags:
        print(i.tag_id, i.tag, i.freq)

这并不等同于GROUP BY tag_id ORDER BY COUNT(tag_id) DESC。为什么?
使用Django ORM,我正在尝试这样做:

SELECT tag_id, COUNT(tag_id) AS freq
FROM taggit_taggeditem
WHERE content_type_id IN (
    SELECT id FROM django_content_type
    WHERE app_label = 'reviews'
    AND model IN ('problem', 'review', 'comment')
) AND (
    object_id = 1
    OR object_id IN (
        SELECT id FROM review
        WHERE problem_id = 1
    ) OR object_id IN (
        SELECT c.id FROM comment AS c
        INNER JOIN review AS r
        ON r.id = c.review_id
    )
) 
GROUP BY tag_id
ORDER BY freq DESC;

这就是我设计的:

querydict_for_content_type_id = {
        'current_app_label_query' : Q(app_label=ReviewsConfig.name),
        'model_name_query' : Q(model__in=['problem', 'review', 'comment'])
    }

    # used in content_type_query
    query_for_content_type_id = reduce(operator.__and__, querydict_for_content_type_id.values())

    # Query relevant content_type from TaggedItem model.    
    content_type_query = Q(content_type_id__in=ContentType.objects.filter(query_for_content_type_id))

    # Query relevant object_id from TaggedItem model.
    object_query = Q(object_id=pk) | Q(object_id__in=problem.review_set.all())
    for review in problem.review_set.all():
        object_query |= Q(object_id__in=review.comment_set.all())

    tagged_items = TaggedItem.objects.filter(content_type_query&object_query)

    # JOIN Tag
    # tags = tagged_items.select_related('tag')
    
    # GROUP BY freq ORDER_BY freq DESC;
    ordered_tags = tagged_items.annotate(freq=Count('tag_id'))#.order_by('-freq')
    for i in ordered_tags:
        print(i.tag_id, i.tag, i.freq)

Django ORM无法正常工作。在ordered_tags上调用distinct方法也不起作用。
输出:

18 tag1 1
19 tag2 1
20 tag3 1
...
18 tag1 1

为什么我得到两次18 tag1 1而不是18 tag1 2

nuypyhwy

nuypyhwy1#

.annotate()完全不等于GROUP BY
通常使用Django的ORM来翻译SQL查询不是一个好主意。它的目的是帮助你解决一个问题,把你的想法转化为查询。
将TaggedItem示例作为包含GROUP BY语句的SQL查询的结果不会有任何意义,因为这些示例不代表TaggedItem表的一行。
要获得GROUP BY语句,需要通过强制结果为dict来摆脱模型层。

qs = TaggedItem.objects.all()
qs = qs.values("tag_id")  # Any field of the TaggedItem table you want as entries in the result dict
qs = qs.annotate(freq=Count("tag_id"))  # This, combined with the above line, will produce a group by tag_id
qs = qs.order_by("-freq")

相关问题