2013-03-13 3 views
11

우리는 prefetch_related으로 애플리케이션 속도를 높이려고합니다. GenericForeignKey 관계를 따를 수 있으며 __으로 더 깊게 갈 수 있지만 관련 모델에 해당 필드가 없으면 불행히도 실패합니다. 여기 동일한 DB 열의 여러 입력란

class ModelA(models.Model): 
    event_object = models.ForeignKey(SomeModelA) 

class ModelB(models.Model): 
    event = models.ForeignKey(SomeModelB) 

class ModelC(models.Model): 
    content_type = models.ForeignKey(ContentType) 
    object_id = models.PositiveIntegerField() 
    content_object = generic.GenericForeignKey() 

그래서 ModelC 인스턴스가 ModelA 또는 ModelB에 하나를 가리킬 수 있습니다 모델 구조의 몇 가지 예입니다. 그리고 둘 다 A와 B 모델을 프리 페치 등의 검색어를 사용할 수 있습니다 ModelC.objects.all().prefetch_related('content_object') 불행히도 나는 또한 이벤트 객체를 선택해야합니다 (SomeModelA 또는 SomeModelB)

내가

ModelC.objects.all().prefetch_related('content_object', 'content_object__event_object') 

그것은 작동을 실행하려고하면 경우 I 인스턴스가 을 가리키고 있지만, ModelB 필드에 event_object 필드가없고 event이 대신 있기 때문에 오류가 발생합니다.

이 모델은 코드의 여러 위치에서 사용되므로 필드의 이름을 바꾸는 것은 좋지 않습니다. 따라서 필드/열에 대한 별칭을 만드는 방법이 있는지 궁금합니다.

나는이 같은 일을하려고했다 :

class ModelB(models.Model): 
    event = models.ForeignKey(SomeModelB) 
    event_object = models.ForeignKey(SomeModelB, db_column='event_id', related_name='+') 

는 DB 테이블에 동일한 열을 가리 두 개의 필드를 확인합니다. 그러나 이것은 save 방법을 깨뜨리는만큼 작동하지 않습니다. Django는 UPDATE 하나의 열이 두 번 위치하고 DatabaseError를 가져 오는 SQL 쿼리를 만듭니다.

그런 별칭을 만들 수있는 방법이 있습니까? 아니면 예외를 던지지 않도록 prefetch_related을 만드는 또 다른 해결책이 있습니까?

업데이트는 : save 방법이 필드를 제외하는 데 사용할 수있는 update_fields 매개 변수가있다. 그러나 1.5에서 도입되었고 1.4를 사용하고 있습니다. 그래서 나는 계속해서 답을 찾으려고 노력합니다.

업데이트 # 2 : @ shx2가 추적을 제공하도록 요청했습니다. 추적 가능성은 2 가지입니다. 1 - 속성은 첫 번째 개체에 없을 때 :

Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 72, in __repr__ 
    data = list(self[:REPR_OUTPUT_SIZE + 1]) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 97, in __iter__ 
    len(self) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 89, in __len__ 
    self._prefetch_related_objects() 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 570, in _prefetch_related_objects 
    prefetch_related_objects(self._result_cache, self._prefetch_related_lookups) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1664, in prefetch_related_objects 
    (attr, first_obj.__class__.__name__, lookup)) 
AttributeError: Cannot find 'event_object' on ModelB object, 'content_object__event_object' is an invalid parameter to prefetch_related() 

그리고 prefetch_related 매개 변수는 다음 첫 번째 개체에 대한 유효 만약 내가 2 역 추적을 얻을

:

Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 72, in __repr__ 
    data = list(self[:REPR_OUTPUT_SIZE + 1]) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 97, in __iter__ 
    len(self) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 89, in __len__ 
    self._prefetch_related_objects() 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 570, in _prefetch_related_objects 
    prefetch_related_objects(self._result_cache, self._prefetch_related_lookups) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1680, in prefetch_related_objects 
    obj_list, additional_prl = prefetch_one_level(obj_list, prefetcher, attr) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1803, in prefetch_one_level 
    qs = getattr(obj, attname).all() 
AttributeError: 'ModelB' object has no attribute 'event_object' 
+0

'prefetch_related' 오류의 추적을 추가 할 수 있습니까? – shx2

+0

왜 ModelC 쿼리 세트가 필요합니까? 두 가지 다른 쿼리를 만들어 따로 따로 처리 할 수 ​​없습니까? 나는 내 질문이 조금 순진하다는 것을 알고 있지만 때때로 문제는 우리가이 어려움에 직면하는 방법이다. – marianobianchi

+0

@marianobianchi 아니, 우리는 장고 관리자의 일부분을 최적화하려고한다. 그래서 우리는 단일 쿼리 세트가 필요하다. 또한 위의 모델은 단순화되어 실제 프로젝트에서 더 깊은 관계가 있습니다 – Igor

답변

2

그것은 버그 감독처럼 보인다 장고에. 이 문제를 해결하기 위해 2 단계 프리 페치를 수행하는 사용자 정의 관리자를 정의 해 볼 수 있습니다.

from django.db import models 
from django.db.models import Q 
from django.contrib.contenttypes.models import ContentType 

class PrefetchWorkaroundManager(models.Manager): 
    def get_queryset(self): 
     q = super(PrefetchWorkaroundManager, self).get_queryset() 
     content_typeA = ContentType.objects.get_for_model(ModelA) 
     content_typeB = ContentType.objects.get_for_model(ModelB) 
     return q.filter(content_type__pk = content_typeA.id).prefetch_related('content_object', 'content_object__event_object') | \ 
       q.filter(content_type__pk = content_typeB.id).prefetch_related('content_object', 'content_object__event') 

class ModelC(models.Model): 
    ... 

    objects_prefetched = PrefetchWorkaroundManager() 

ModelC.objects 대신 ModelC.objects_prefetched에 액세스해야 자리를 차지할 프리 페치를 원하는 각 발신자 :

ModelC.objects_prefetched.filter(...) 

내가 아마 그대로 작동하지 않는, 그래서 나는 그것을 테스트하지 않았다, 인정한다. 그러나 나는이 접근법이 건전하다고 믿습니다.

+0

답장을 보내 주셔서 감사합니다.하지만 불행히도 작동하지 않습니다. 쿼리 세트에서'|'를 사용하면 새로운 쿼리 세트를 빌드합니다. 'prefetch_related'는 쿼리 세트 자체가 평가 될 때 평가됩니다. 또한 장고에 버그가 있는지 확실하지 않지만,'|'를 사용하면 첫 번째 쿼리에서만 프리 페치 조회를 사용합니다 – Igor