Django使用子查询计数来注释查询集

时间:2009-12-05 01:25:54

标签: django django-models

这似乎在django 1.1中不起作用(我相信这将需要子查询,因此标题出现)

qs.annotate(interest_level= \
             Count(Q(tags__favoritedtag_set__user=request.user))
           )

我的查询集中有一些项目被标记,标签可以被用户收藏,我想计算用户通过标签在集合中偏好每个项目的次数。

有没有办法在不使用extra()?

的情况下构造这样的查询

感谢。

2 个答案:

答案 0 :(得分:10)

查看add_aggregate中的django/db/models/sql/query.py函数,不会接受查询对象作为输入值。

不幸的是,目前Django中没有直接的方法来聚合/注释查询集的数量,特别是不会以某种方式进行额外过滤。

假设有以下型号:

class Item(models.Model):
    name = models.CharField(max_length=32)

class Tag(models.Model):
    itemfk = models.ForeignKey(Item, related_name='tags')
    name = models.CharField(max_length=32)

class FavoritedTag(models.Model):
    user = models.ForeignKey(User)
    tag = models.ForeignKey(Tag)

此外,您无法在通过.extra()定义的字段上注释查询集。

可以像views.py一样进入SQL:

from testing.models import Item, Tag, FavoritedTag
from django.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required
from django.utils.datastructures import SortedDict

@login_required
def interest_level(request):
    ruid = request.user.id

    qs = Item.objects.extra(
        select = SortedDict([
            ('interest_level', 'SELECT COUNT(*) FROM testing_favoritedtag, testing_tag \
            WHERE testing_favoritedtag.user_id = %s \
            AND testing_favoritedtag.tag_id = testing_tag.id \
            AND testing_tag.itemfk_id = testing_item.id'),
        ]),
        select_params = (str(ruid),)
    )

    return render_to_response('testing/interest_level.html', {'qs': qs})

模板:

{% for item in qs %}
    name: {{ item.name }}, level: {{ item.interest_level }}<br>
{% endfor %}

我使用MySQL5测试了这个。因为我不是SQL专家,所以我很好奇如何在这里进行优化,或者是否有另一种方法来“减少”SQL的数量。也许有一些有趣的方法可以直接在SQL中使用related_name功能?

答案 1 :(得分:1)

如果你想避免使用原始SQL,另一种方法是使用模型方法,然后在模型中使用模型方法为模板提供新属性。未经测试,但您的Tags模型上的此类内容应该有效:

class Tag(models.Model):
    itemfk = models.ForeignKey(Item, related_name='tags')
    name = models.CharField(max_length=32)

    def get_favetag_count(self):
        """
        Calculate the number of times the current user has favorited a particular tag
        """

        favetag_count = FavoritedTag.objects.filter(tag=self,user=request.user).count()
        return favetag_count

然后在您的模板中,您可以使用以下内容:

{{tag}} ({{tag.get_favetag_count}})

这种方法的缺点是,如果你处于一个大循环或其他什么东西,它可能会更多地打击数据库。但总的来说它运行良好,并且无法使用注释来对相关模型进行查询。并且避免使用原始SQL。