2009-09-08 14 views
14

Djangoで食糧ログデータベースを構築していますが、クエリに関連する問題があります。Djangoの関連モデルでannotated Count()をソートする方法

消費モデルを使用して、M2Mフィールドの「消費者」を通じてUserモデルに接続されたFoodモデルを含むようにモデルを設定しました。フードモデルはフードディッシュを記述し、消費モデルはユーザーのフードの消費量(日付、量など)を表します。

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") 

私は食品オブジェクトは、そのユーザのための消費テーブルに表示された回数(回数ユーザーが食糧を消費している)が注文したすべての食品オブジェクトを返すクエリを作成したいです。

私はのラインで何かをしようとしている:

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

しかし、これはもちろん、ユーザーに関連付けられているものだけでなく、食品のオブジェクトに関連するすべての消費オブジェクトを数えます。私のモデルを変更する必要があるのですか、それとも質問に明白なものがないのですか?

これは非常に重要な操作です(とりわけ、フロントエンドのオートコンプリートフィールドを埋めるために使用されます)。フードテーブルには数千のエントリがありますので、データベースで並べ替えを実行します終わり、強引な方法をやってやって結果を反復処理するのではなく:

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

をし、それらを並べ替えるためのpythonの並べ替えを使用しました。ユーザーベースが増えるにつれて、その方法が非常にうまくスケールされるとは思わないし、私は最初からできるように将来の証明としてデータベースを設計したいと思う。

アイデア?

+0

の可能性のある重複した[ForeignKeyのフィールドのカウント順?](http://stackoverflow.com/questions/2501149/order-by-count-of-a-foreignkey-field) –

答えて

21

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

Food.objects.filter(consumer__user=user)\ 
      .annotate(consumption_times=Count('consumer'))\ 
      .order_by('consumption_times') 
+0

しかし、これは希望しばらくして消費された食物だけを返すのですか?私はすべての食べ物のオブジェクトを返すが、最も頻繁に最初に消費される順にしたい。 私がユーザーによってフィルタリングすると、まだ消費されていない食品は手に入れられません。 1つのアイデアは2つのクエリを実行することです。最初は、少なくとも1回消費されたすべてのFoodアイテムを取得し、次にFood.objects.exclude(consumer__user = user)の行に沿って何かを取得し、 。それは働くだろうか? –

+0

ええ、2つのクエリは私がそれをやる方法でしょう。 – SmileyChris

19

私は非常に似た問題を抱えています。私が望む何

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式に注釈を付け、ための操作/機能の豊かなセットを持っている可能性があることです。基本的に、私はあなたがしたいSQLクエリがあることを知っています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') 
+0

これは素晴らしいです!ありがとうございました。私はそれが少しよかったようにしようとしますが、間違いなくこれをdjangoのtracに登録するべきです。 –

関連する問題