2016-07-26 9 views
0

私はDjango ORMを使用してこのクエリを作成しようとすると壁に頭を叩いていましたが、どこでも検索しましたが答えは見つかりませんでした。 これは私のモデルです。Djangoの従属サブクエリ

class Decision(models.Model): 
    ACTION_CHOICES = [('include', _('Include')), ('exclude', _('Exclude'))] 
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 
    date_taken = models.DateTimeField(_('date taken'), default=timezone.now) 
    action = models.CharField(max_length=7, choices=ACTION_CHOICES) 
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 
    object_id = models.PositiveIntegerField() 
    content_object = GenericForeignKey('content_type', 'object_id') 
    project = models.ForeignKey('projects.Project', on_delete=models.CASCADE) 
    is_permanent = models.BooleanField(_('is permanent'), default=False) 

そして、私は照会する欲しいものを得るためにDecision.filter(user_id=user.pk, project_id=project.pk)のような単純なクエリセットを反復処理するこのクエリは、アプリケーション内で繰り返し呼び出されているため、ユーザは、プロジェクト内で行われていることをすべてのcontent_objectのための最新の永続的な決定であり、最終結果はオプションではありません。これまでのところ、このようなRawQuerySetを使って解決しました。

Decision.objects.raw("SELECT decision.id, decision.content_type_id, decision.object_id " 
        "FROM canvasblocks_decision AS decision " 
        "WHERE decision.date_taken = (SELECT max(last_decision.date_taken) " 
        "        FROM canvasblocks_decision AS last_decision " 
        "        WHERE last_decision.object_id = decision.object_id AND " 
        "         last_decision.content_type_id = decision.content_type_id AND " 
        "         last_decision.project_id = %s AND decision.user_id = %s) ", 
        [project.pk, user.pk]) 

しかし、私はすべてのDjangoのORMのパワーを失い、私はアクションを確認するために、再びこのクエリをフィルタ処理する必要があるため、その力は私が上でこれを変換する必要が原因それは、苦痛だ失うので、私は、このソリューションを好きではありませんサブクエリには複雑さが伴います。だから、みんな、これを行う良い方法を知っている?私はDjango 1.9.4を使用しています。前もって感謝します。

答えて

0

データセットが小さい場合、複数のパスを作成できますが、それはあまり良い考えではありません。

いくつかのオプションは、使用しているデータベースによって異なります。

1)sqlをrankまたはdense_rank関数を使用して変更すると、クエリがはるかに簡単になります。

2)アノテーションに同じロジックを入れてランクを取得できます。そうすれば、あなたはdjangoオブジェクトが持つすべてのものを手に入れることができます。

from django.db.models.expressions import RawSQL 
Decision.objects.filter().annotate(rank=RawSQL("RANK() OVER (partition by id, content_type, object_id 
            (ORDER BY date_taken DESC)", []) 
           ) 

..

これは役立つかもしれない:https://stackoverflow.com/a/35948419/237939

+0

おかげ@Rajesh Chamarthiを。私はDBをPostgresに移行する必要があります!非常に多くの素晴らしい機能!あなたが私に与えた最初のオプションを完全に理解していない、あなたの答えの[postrgresウィンドウ関数](https://www.postgresql.org/docs/devel/static/functions-window.html)はまったく新しいものです私は、 'first_value(...)'がパーティション内で最も高いランクを持つ行を取ると思いますが、 'last_decision.date_taken'がどこから来るのかわかりません.2番目のオプションでは、 '.filter(rank = 1)'最終結果を得るには? –

+0

はい、ランク= 1にフィルタリングする必要があります。私はまた、パーティションの句を追加する答えを編集しました。申し訳ありませんが、私はDjangoでインスタンスへのアクセス権がないため、現時点ではテストされていません。 Postgresは素晴らしいオプションです。 –

+0

心配しないで、私は後でそれを試してみるつもりです、私は最終的に、[このビデオ](https://www.youtube.com/watch?v=KwEjkpFltjc)が、 OVER(PARTITION BY ...) 'は、私のサブクエリが' GROUP BY'だったのと同じように動作しますが、もっと単純です。この場合、より最近の要素の各行decision.date_takenを追加し、マッチを選ぶのはどこですか?もし私が間違っていれば、私は 'OVER(PARTITION BY ...) 'へのエイリアスがなく、' last_decision.date_taken'ではなく 'decision.date_taken'でしょうか?再度、感謝します。 –