代码之家  ›  专栏  ›  技术社区  ›  Evgeny

使用子查询计数的django注释查询集

  •  15
  • Evgeny  · 技术社区  · 16 年前

    这在Django1.1中似乎不起作用(我相信这需要一个子查询,因此出现了标题)。

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

    我的查询集中有标记的项目,用户可以喜欢标记,我想计算用户通过标记喜欢集合中每个项目的次数。

    有没有一种方法可以在不使用extra()的情况下构造这样的查询?

    谢谢。

    2 回复  |  直到 16 年前
        1
  •  10
  •   Geradeausanwalt    16 年前

    看着 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() .

    一个可以插入到SQL中 views.py 就像这样:

    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的数量。也许有一些有趣的方法来利用 related_name 这里的特性直接在SQL中?

        2
  •  1
  •   shacker kravietz    16 年前

    如果您想避免落入原始SQL中,另一种消除这类问题的方法是使用模型方法,该方法将为您提供模型上要在模板中使用的新属性。未经测试,但在您的标签模型上应该可以这样做:

    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。