2009-05-30 6 views
1

Projectfundingdetailには、プロジェクトには外部キーがあります。Django ORM特定のキーインスタンスを制限するクエリ

次のクエリでは、のプロジェクトの一覧が表示されます。プロジェクトの詳細は1000未満です。どのようにプロジェクトの最新の詳細に限定するのですか。

projects_list.filter(projectfundingdetail__budget__lte=1000).distinct() 

私は

def latest_funding(self): 
    return self.projectfundingdetail_set.latest(field_name='end_date') 

、次の関数を定義している。しかし、私はlatest_fundingとして以下を使用カントは、データベースフィールド

projects_list.filter(latest_funding__budget__lte=1000).distinct() 

ではありませんので、私はすべて取得するにはどのようなクエリを使用する必要があります最新のプロジェクト資金のみが1000未満のプロジェクト。

答えて

3

このクエラー一見すると、見た目よりも難しいです。 AFAIK Django ORMは効率的なSQLには相関サブクエリが必要なため、このクエリに対して効率的なSQLを生成する方法はありません。あなたはこのクエリをいくつかの醜いSQLを生成することができます(!私はこの上で修正するのが大好きです):

Projectfundingdetail.objects.annotate(latest=Max('project__projectfundingdetail__end_date')).filter(end_date=F('latest')).filter(budget__lte==1000).select_related() 

をしかし、これはために、おそらく十分なのに(非効率的である、再びプロジェクトとするProjectfundingdetailから参加することが必要ですあなたの要望)。

これを行うもう1つの方法は、生のSQLを書き込んで、それをマネージャメソッドにカプセル化することです。それは少し怖いが、素晴らしい作品です。 Projectfundingdetailの属性を「オブジェクト」としてあなたがマネージャーを割り当てる場合は、各プロジェクトの最新の資金調達の詳細を取得するには、このようにそれを使用することができます:

>>> Projectfundingdetail.objects.latest_by_project() 

そして、あなたはさらにフィルターに追加することができますので、それは、通常のクエリセットを返します。 :

​​

ここでは、コードです:そのコード(「名前」の辞書)の約半分

from django.db import connection, models 
qn = connection.ops.quote_name 

class ProjectfundingdetailManager(models.Manager): 
    def latest_by_project(self): 
     project_model = self.model._meta.get_field('project').rel.to 

     names = {'project': qn(project_model._meta.db_table), 
       'pfd': qn(self.model._meta.db_table), 
       'end_date': qn(self.model._meta.get_field('end_date').column), 
       'project_id': qn(self.model._meta.get_field('project').column), 
       'pk': qn(self.model._meta.pk.column), 
       'p_pk': qn(project_model._meta.pk.column)} 

     sql = """SELECT pfd.%(pk)s FROM %(project)s AS p 
       JOIN %(pfd)s AS pfd ON p.%(p_pk)s = pfd.%(project_id)s 
       WHERE pfd.%(end_date)s = 
        (SELECT MAX(%(end_date)s) FROM %(pfd)s 
         WHERE %(project_id)s = p.%(p_pk)s) 
       """ % names 

     cursor = connection.cursor() 
     cursor.execute(sql) 
     return self.model.objects.filter(id__in=[r[0] for r 
               in cursor.fetchall()]) 

することは、非標準の可能性に対して堅牢であることが必要なだけですデータベース表および列名。テーブルとカラムの名前を変更することはないと確信している場合は、SQLにテーブル名とカラム名をハードコードすることもできます。

+0

ありがとうございます。それを検証してくれて、それが複雑になることは分かっていました。最初の複数の結合クエリであっても、annotate()関数はトランクでのみ使用できます。タグ付きリリースではありません。 –

+0

それ以外の場合は、プロジェクトが最新であるかどうかを格納するモデルにis_latestフィールドを追加し、別のdbフィルタ条件としてis_latest = trueを追加することもできます。どのように良い(または悪い)このアプローチです。 –

+0

annotate()は、今後リリースされる1.1リリース(タグ付き1.1ベータ版で既に利用可能)で利用可能になります。 raw SQLのアプローチは、完全に1.0と互換性があります。そして、はい、非正規化アプローチは、それぞれの場合の書き込みと読み取りのバランスと読み取りのニー​​ズに応じて、考慮するもう1つの良いオプションになります。あなたのユースケースを実際にベンチマークすることなく、良い決断を下す方法はありません。 –

関連する問題