2009-05-30 5 views
1

Projectfundingdetail에 외래 키가 있습니다.Django ORM 특정 키 인스턴스를 제한하는 쿼리

다음 쿼리는 인 프로젝트의 목록을 제공합니다.이 프로젝트의 세부 프로젝트 내용은입니다. 어떻게 프로젝트를 최신 프로젝트 세부 사항으로 제한합니까?

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

이 쿼리 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()]) 

비표준의 가능성에 대한 강력한 될에만 필요

: 여기
>>> Projectfundingdetail.objects.latest_by_project().filter(budget__lte=1000) 

코드입니다 데이터베이스 테이블 W 컬럼 이름. 또한 테이블과 열 이름을 변경하지 않을 것이라고 확신하는 경우 SQL에 하드 코딩 할 수도 있습니다.

+0

감사합니다. 그것을 확인해 주셔서 감사합니다. 초기 다중 결합 쿼리의 경우에도 annotate() 함수는 트렁크에서만 사용할 수 있습니다. 태그가 추가 된 릴리스에는 없습니다. –

+0

그렇지 않으면 프로젝트가 최신인지를 저장하고 is_latest = true를 다른 db 필터 조건으로 포함하는 모델에 is_latest 필드를 간단히 추가 할 수 있습니다. 이 접근 방식은 얼마나 좋은 (또는 나쁜) 것입니다. –

+0

annotate()는 곧 출시 될 1.1 릴리스에서 사용할 수 있습니다 (태그가있는 1.1 베타에서 이미 사용 가능). 원시 SQL 접근법은 완전히 1.0 호환 가능하다고 생각합니다. 그리고 예, 비정규 화 접근법은 쓰기와 읽기의 균형과 각 경우의 성능 요구에 따라 고려해야 할 또 다른 좋은 옵션입니다. 실제로 유스 케이스를 벤치마킹하지 않고 올바른 결정을 내릴 수 없습니다. –