2009-10-29 8 views
5

을 포함하는 최적화 쿼리 나는 다음과 같은 모델 구조를 가지고 :장고 ORM : 다 대다 관계

class Container(models.Model): 
    pass 

class Generic(models.Model): 
    name = models.CharacterField(unique=True) 
    cont = models.ManyToManyField(Container, null=True) 
    # It is possible to have a Generic object not associated with any container, 
    # thats why null=True 

class Specific1(Generic): 
    ... 

class Specific2(Generic): 
    ... 

... 

class SpecificN(Generic): 
    ... 

말, 나는 특정 컨테이너와 관계가있는 모든 Specific 타입 모델을 검색 할 필요가있다.

SQL은 그다지 사소하지만 그다지 문제되지 않습니다. 불행히도, 나는 ORMs (Django의 ORM) 작업에 대해서는 경험이별로 없기 때문에 여기서 패턴을 찾을 수 없을지도 모른다.

무차별 대입 방식으로 수행

-

c = Container.objects.get(name='somename') # this gets me the container 
items = c.generic_set.all() 
# this gets me all Generic objects, that are related to the container 
# Now what? I need to get to the actual Specific objects, so I need to somehow 
# get the type of the underlying Specific object and get it 
for item in items: 
    spec = getattr(item, item.get_my_specific_type()) 

이 (컨테이너 관련 각 일반 레코드 하나) DB 안타의 톤 결과, 그래서 이것은 분명히 방법이 아니다 그것을하기 위해. 이제, 그것은 아마의 SpecificX을 받고 수행 할 수있는 직접 객체 : DB를 각 특정 유형 (허용, 나는 추측)에 대해 한 번 타격을받을 것입니다

s = Specific1.objects.filter(cont__name='somename') 
# This gets me all Specific1 objects for the specified container 
... 
# do it for every Specific type 

그런 식으로.

나는 그 .select_related()가 m2m 관계에서 작동하지 않는다는 것을 알고있다. 그래서 여기서 많은 도움이되지는 않는다.

다시 말하면 최종 결과는 SpecificX 개체의 컬렉션이어야합니다 (일반이 아님).

+0

는 이제 문제는 나에게 의미가 조금 보인다. 결국 임의의 필드가있는 여러 테이블에서 공동 결과를 얻을 수있는 방법이 없습니다. 그래, 거기에 분명히 있지만, 그것은 추한 및 동적 SQL 및/또는 "선택 *"가 포함됩니다. 나는 생각한다. – shylent

+0

귀하의 질문은 실제로 ManyToMany 관계를 최적화하는 것과 아무런 관련이 없으며 다중 테이블 상속에 대해 쿼리를 최적화하는 것과 관련이 있습니다. 참으로 어려운 문제입니다. –

+0

이 문제는 m2m 관계와 관련이 있다고 생각하게되었습니다. 실제로 select_related가 many-to-many 관계를 횡단하지 않는다는 사실입니다. – shylent

답변

2

나는 두 가지 쉬운 가능성을 이미 설명했다고 생각합니다. Generic에 대해 단일 필터 쿼리를 수행 한 다음 각 항목을 특정 하위 유형 (n + 1 쿼리 결과, n은 반환되는 항목 수)으로 캐스팅하거나 각 특정 테이블에 대해 별도의 쿼리를 만듭니다 (k 쿼리, 여기서 k는 특정 유형의 수).

사실 실제로 이들 중 어느 것이 더 빠른 지 벤치마킹 할 가치가 있습니다. 두 번째 쿼리는 (아마도) 쿼리가 적기 때문에 더 좋아 보이지만, 각 쿼리는 m2m 중간 테이블과 조인을 수행해야합니다. 전자의 경우에는 하나의 조인 쿼리 만 수행 한 다음 많은 간단한 쿼리를 수행합니다. 일부 데이터베이스 백엔드는 작고 복잡한 쿼리보다 작은 쿼리를 많이 사용하면 성능이 향상됩니다.

두 번째 코드가 실제로 사용 사례에 비해 훨씬 빠르며 코드를 정리하기 위해 추가 작업을 수행하려는 경우 일반 모델에 대한 사용자 지정 관리자 메서드를 작성하여 " 서브 타입 테이블 당 하나의 쿼리만을 사용하여 주어진 쿼리 세트에 대한 관련 테이블의 모든 서브 타입 데이터를 가져옵니다. this snippet이 대량 프리 페치로 일반 외래 키를 최적화하는 방법과 비슷합니다. 이렇게하면 첫 번째 옵션의 DRYer 구문을 사용하여 두 번째 옵션과 동일한 쿼리가 제공됩니다.

+0

누가 이것을 downvoted 모르겠다. 그리고 예, 커스텀 매니저를 작성할 가능성에 대해서 살펴볼 것입니다. 그러나 ORM에 대한 저의 경험은 매우 제한적입니다. (저는 SQL에 아무런 문제가 없습니다.) 그래서 내부는 여전히 약간 있습니다. 나에게 블랙 박스. 어쨌든, 나는 내가 할 수있는 것을보고 갈 것이다. – shylent

1

하지 완전한 대답하지만 당신이 대신이

items= list(items) 
for item in items: 
    spec = getattr(item, item.get_my_specific_type()) 

을 수행하여 히트의 큰 숫자를 방지 할 수 있습니다

for item in items: 
    spec = getattr(item, item.get_my_specific_type()) 

을 실제로 파이썬 목록에 깁스를 강제로 당신을 django orm이 쿼리 세트의 모든 요소를로드하도록합니다. 그런 다음 하나의 쿼리에서이 작업을 수행합니다.

+0

이것은 좋은 힌트입니다 (내가 읽은 문서에 언급되어 있습니다 :). 그리고 너무도 명백하지 않습니다. – shylent

+3

음, 이것은 사실이 아닙니다. 목록에 캐스팅하면이 경우 아무런 차이가 없습니다. 두 버전 모두 일반 테이블에 대해 하나의 쿼리 만 수행되며 두 버전 모두 각 항목의 SpecificX 테이블에 대해 하나의 쿼리가 수행됩니다. 둘 다 동일한 수의 쿼리. –