インスタンスまたはインスタンスのいずれかを指すモデルBox
があるとします。とは、それぞれtoとForeignKeysを持っています。アクセスする必要のあるesのリストを表示したい。できるだけ少ないDBクエリでこれを行うにはどうすればよいですか?GenericForeignKey
Apple
Chocolate
Apple
Chocolate
Farm
Factory
Box
Farm
Factory
最小限の実例:
class Farm(Model):
...
class Apple(Model):
farm = ForeignKey(Farm)
...
class Factory(Model):
...
class Chocolate(Model):
factory = ForeignKey(Factory)
...
class Box(Model)
content_type = ForeignKey(ContentType)
object_id = PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
...
def __unicode__(self):
if self.content_type == ContentType.objects.get_for_model(Apple):
apple = self.content_object
return "Apple {} from Farm {}".format(apple, apple.farm)
elif self.content_type == ContentType.objects.get_for_model(Chocolate):
chocolate = self.content_object
return "Chocolate {} from Factory {}".format(chocolate, chocolate.factory)
これが私が試したいくつかのことです。これらすべての例で、Nはボックスの数です。ContentType
クエリカウントは、Apple
とChocolate
がすでにキャッシュされていることを前提としているため、get_for_model()
呼び出しはDBにヒットしません。
1)ナイーブ:
print [box for box in Box.objects.all()]
これは、 1(ボックスをフェッチ)+ N(ボックスごとにAppleまたはChocolateをフェッチ)+ N (AppleごとにFarmをフェッチし、ChocolateごとにFactoryをフェッチ)クエリを実行します。
2)はであるselect_related
ため、ここでは役に立ちません。Box.content_object
GenericForeignKey
3)django 1.4以降、 sprefetch_related
をフェッチできGenericForeignKey
ます。
print [box for box in Box.objects.prefetch_related('content_object').all()]
これは、 1(ボックスをフェッチ)+ 2(すべてのボックスのアップルとチョコレートをフェッチ)+ N(各アップルのファームと各チョコレートのファクトリーをフェッチ)クエリを実行します。
4)どうやらprefetch_related
、GenericForeignKeysのForeignKeysをフォローするほど賢くはありません。私が試してみると:
print [box for box in Box.objects.prefetch_related(
'content_object__farm',
'content_object__factory').all()]
Chocolate
オブジェクトにフィールドがないことfarm
、およびその逆のことは当然のことです。
5)私ができること:
apple_ctype = ContentType.objects.get_for_model(Apple)
chocolate_ctype = ContentType.objects.get_for_model(Chocolate)
boxes_with_apples = Box.objects.filter(content_type=apple_ctype).prefetch_related('content_object__farm')
boxes_with_chocolates = Box.objects.filter(content_type=chocolate_ctype).prefetch_related('content_object__factory')
これは、 1(ボックスをフェッチ)+ 2(すべてのボックスのリンゴとチョコレートをフェッチ)+ 2(すべてのリンゴのファームとすべてのチョコレートのファクトリーをフェッチ)クエリを実行します。欠点は、2つのクエリセット(boxes_with_apples
、boxes_with_chocolates
)を手動でマージして並べ替える必要があることです。私の実際のアプリケーションでは、これらのボックスをページ付けされたModelAdminに表示しています。このソリューションをそこに統合する方法は明らかではありません。たぶん私はこのキャッシングを透過的に行うためのカスタムPaginatorを書くことができますか?
6)これに基づいてO(1)クエリも実行する何かをまとめることができます。_content_object_cache
しかし、それを避けることができれば、内部()をいじりたくありません。
要約:ボックスを印刷するには、GenericForeignKeyのForeignKeysにアクセスする必要があります。O(1)クエリでNボックスを印刷するにはどうすればよいですか?(5)私ができる最善のことですか、それとももっと簡単な解決策がありますか?
ボーナスポイント:このようなクエリを簡単にするために、このDBスキーマをどのようにリファクタリングしますか?