2009-05-30 53 views
1

Projectfundingdetail有一個外鍵用於投影。Django ORM查詢限制特定鍵實例

以下查詢爲我提供了任何 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() 

所以我應該用什麼樣的查詢來獲取所有項目,只有他們的最新projectfunding低於1000.

回答

3

此查詢y比乍一看更難。 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加入到項目,然後再返回,這是低效的(雖然也許足夠您的需求)。

另一種方法是編寫原始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()函數也只能在中繼中使用。不在任何標籤發佈。 – 2009-05-30 18:16:15