1

Projectfundingdetail には、プロジェクトへの外部キーがあります。

次のクエリは、 projectfundingdetail が 1000 未満のすべてのプロジェクトのリストを表示します。最新の projectfundingdetail のみに制限するにはどうすればよいですか。

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

では、最新の projectfundingdetail のみが 1000 未満のすべてのプロジェクトを取得するには、どのクエリを使用すればよいでしょうか。

4

1 に答える 1

3

このクエリは、一見したよりも難しいものです。私の知る限り、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 から Project に参加し、再び Project に戻る必要があり、非効率的です (ただし、ニーズにはおそらく十分です)。

これを行うもう 1 つの方法は、生の SQL を記述し、マネージャー メソッドにカプセル化することです。少し怖いように見えますが、うまく機能します。Projectfundingdetail でマネージャーを「オブジェクト」属性として割り当てると、次のように使用して、各プロジェクトの最新の資金調達の詳細を取得できます。

>>> Projectfundingdetail.objects.latest_by_project()

通常の QuerySet を返すため、さらにフィルターを追加できます。

>>> Projectfundingdetail.objects.latest_by_project().filter(budget__lte=1000)

コードは次のとおりです。

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 にハードコードすることもできます。

于 2009-05-30T17:38:58.720 に答える