DjangoのORMのクエリは、特定のキーインスタンスに対して制限するために、
-
06-09-2019 - |
質問
Projectfundingdetailは、プロジェクトへの外部キーを持っています。
次のクエリは、1000の下で私にのいずれかののprojectfundingdetailを持っているすべてのプロジェクトのリストが表示されますどのように私は、最新の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()
だから私は1000年の下でのみ、最新のprojectfundingdetailを持っているすべてのプロジェクトを取得するために使用すべきかを問い合わせます。
解決
このクエリは、それは一見見た目より難しいです。効率的なSQLは、相関サブクエリを必要とするので、私の知る限りDjangoのORMは、このクエリの効率的なSQLを生成する方法を提供していません。 (私はこれを修正するのが大好きです!)あなたはこのクエリといくつかの醜いSQLを生成することができます:
Projectfundingdetail.objects.annotate(latest=Max('project__projectfundingdetail__end_date')).filter(end_date=F('latest')).filter(budget__lte==1000).select_related()
しかし、これは(あなたのニーズにおそらく十分なが)非効率的である、再びプロジェクトとするProjectfundingdetailから参加する必要があります。
これを行うための他の方法は、生のSQLを記述し、管理方法でそれをカプセル化することです。それは少し怖い見えるが素晴らしい作品。あなたはProjectfundingdetailの「オブジェクト」属性として管理者を割り当てる場合は、各プロジェクトの最新の資金調達の詳細を取得するには、このようにそれを使用することができます:
>>> Projectfundingdetail.objects.latest_by_project()
そして、あなたはさらにフィルターに追加することができますので、それは、通常のクエリセットを返します:
>>> 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にテーブル名とカラム名をハードコーディングできます。