2013-03-13 7 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') 

を実行しようとするとそれがうまくいくかどうかはインスタンスはModelAを指しますが、ModelBにはevent_objectフィールドがなく、代わりにeventがあるため、失敗する可能性があります。

このモデルはコードの多くの場所で使用されているため、フィールドの名前を変更することはお勧めできません。フィールド/列のエイリアスを作成する方法があるのだろうかと思います。

私はこのようにやろうとした:DBテーブルの同じ列を指す2つのフィールドを作るために

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

。しかし、これは、saveメソッドを壊すので動作しません。 DjangoはUPDATE SQLクエリを作成し、1列が2回配置され、DatabaseErrorを取得します。

このようなエイリアスを作成する方法はありますか?または、例外をスローしないようにprefetch_relatedを作成する別の解決方法がありますか?

更新save方法では、このフィールドを除外するために使用することができますupdate_fieldsパラメータがあります。しかし1.5に導入され、1.4を使用しています。だから私は答えを探し続ける。

更新#2:@ shx2がトレースバックを提供するように頼んだ。トレースバックは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 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クエリーセットが必要ですか? 2つの異なるクエリを作成し、それらを別々に扱うことはできませんか?私は私の質問が少し素朴であることを知っているが、ときどき問題はちょうど私たちがこの困難に直面している方法です。 – marianobianchi

+0

@marianobianchiいいえ、私たちはdjangoの管理者の一部を最適化しようとします。上記のモデルも簡素化されており、実際のプロジェクトではより深い関係がある – Igor

答えて

2

それはバグや監督のように見えますdjangoで。この問題を回避するには、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'は、クエリセット自体が評価されるときに評価されます。また、私はそれがDjangoのバグかどうかは分かりませんが、 '|'を使うと最初のクエリからのプリフェッチルックアップだけを使用します – Igor

関連する問題