23

Djangoで食品ロギングデータベースを構築していますが、クエリ関連の問題があります。

私は、消費モデルを介してM2Mフィールドの「消費者」を介してユーザーモデルに接続された食品モデルを(とりわけ)含めるようにモデルを設定しました。Foodモデルは食品料理を記述し、Consumptionモデルはユーザーの食品の消費量(日付、量など)を記述します。

class Food(models.Model):
    food_name = models.CharField(max_length=30)
    consumer = models.ManyToManyField("User", through=Consumption)

class Consumption(models.Model):
    food = models.ForeignKey("Food")
    user = models.ForeignKey("User")

そのユーザーのConsumptionテーブルにFoodオブジェクトが表示された回数(ユーザーが食べ物を消費した回数)順に並べられたすべてのFoodオブジェクトを返すクエリを作成したいと思います。

私は次の行で何かを試しています:

Food.objects.all().annotate(consumption_times = Count(consumer)).order_by('consumption_times')`

ただし、これはもちろん、ユーザーに関連付けられているオブジェクトだけでなく、Foodオブジェクトに関連するすべてのConsumptionオブジェクトをカウントします。モデルを変更する必要がありますか、それともクエリで明らかな何かが欠落しているだけですか?

これはかなりタイムクリティカルな操作であり(特に、フロントエンドのオートコンプリートフィールドに入力するために使用されます)、Foodテーブルには数千のエントリがあるため、データベース側ではなく、データベース側で並べ替えを行います。ブルートフォースメソッドを実行し、次のことを実行して結果を繰り返します。

Consumption.objects.filter(food=food, user=user).count()

次に、Pythonソートを使用してそれらをソートします。ユーザーベースが増えるにつれて、この方法はあまり拡張できないと思います。データベースは、最初から可能な限り将来にわたって利用できるように設計したいと思います。

何か案は?

4

2 に答える 2

38

おそらくこのようなものですか?

Food.objects.filter(consumer__user=user)\
            .annotate(consumption_times=Count('consumer'))\
            .order_by('consumption_times')
于 2009-09-08T21:00:38.527 に答える
22

私は非常によく似た問題を抱えています。基本的に、必要なSQLクエリは次のとおりです。

SELECT food.*, COUNT(IF(consumption.user_id=123,TRUE,NULL)) AS consumption_times
       FROM food LEFT JOIN consumption ON (food.id=consumption.food_id)
       ORDER BY consumption_times;

集計関数とF式を組み合わせて、集計関数なしでF式に注釈を付け、F式の操作/関数のセットを充実させ、基本的に自動F式注釈である仮想フィールドを使用できるようにしたいと思います。あなたができるように:

Food.objects.annotate(consumption_times=Count(If(F('consumer')==user,True,None)))\
            .order_by('consumtion_times')

また、独自の複雑な集計関数をより簡単に追加できるようにすると便利ですが、それまでの間、これを行うために集計関数を追加するハックを次に示します。

from django.db.models import aggregates,sql
class CountIf(sql.aggregates.Count):
    sql_template = '%(function)s(IF(%(field)s=%(equals)s,TRUE,NULL))'
sql.aggregates.CountIf = CountIf

consumption_times = aggregates.Count('consumer',equals=user.id)
consumption_times.name = 'CountIf'
rows = Food.objects.annotate(consumption_times=consumption_times)\
                   .order_by('consumption_times')
于 2009-11-25T20:17:40.433 に答える