15


6

Djangoはサブクエリのカウントでクエリセットに注釈を付けます

これはdjango 1.1では動作しないようです(これにはサブクエリが必要になると思うので、タイトルが来ます)

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

クエリセットにはタグ付きのアイテムがあり、ユーザーがタグをお気に入りに追加できます。タグを使用してセット内の各アイテムをユーザーがお気に入りに追加した回数を計算したいと思います。

extra()を使用せずにこのようなクエリを作成する方法はありますか?

ありがとう。

2 Answer


10


django / db / models / sql / query.py`内の add_aggregate`関数を見ると、クエリオブジェクトは入力値として受け入れられません。

残念ながら、現在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 }}

{% endfor %}

MySQL5を使用してこれをテストしました。 私はSQLの専門家ではないので、ここで最適化する方法、またはSQLの量を「減らす」別の方法があるかどうかに興味があります。 ここで、SQL内で直接「related_name」機能を利用する興味深い方法がありますか?


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を使用する必要がなくなります。