Этот запрос сложнее, чем кажется на первый взгляд. 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 к Project и обратно, что неэффективно (хотя, возможно, и соответствует вашим потребностям).
Другой способ сделать это - написать сырой 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, если вы уверены, что они никогда не изменятся.