質問

次のモデル構造を持っています:

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は多かれ少なかれ自明ですが、それは問題ではありません。残念ながら、私はORM(特に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ヒット(コンテナに関連するGenericレコードごとに1つ)が発生するため、これは明らかにそれを行う方法ではありません。これで、おそらくSpecificXオブジェクトを直接取得することで可能になりました:

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

この方法で、特定のタイプごとにdbが1回ヒットします(許容されると思います)。

.select_related()はm2m関係では機能しないので、ここではあまり役に立ちません。

繰り返しますが、最終結果はSpecificXオブジェクトのコレクションでなければなりません(Genericではありません)。

役に立ちましたか?

解決

2つの簡単な可能性の概要を既に説明したと思います。 Genericに対して単一のフィルタークエリを実行し、各アイテムを特定のサブタイプにキャストするか(n + 1クエリ、nは返されるアイテムの数)、または特定のテーブルに対して個別のクエリを実行します(結果はkになります)クエリ(kは特定のタイプの数)。

これらのどれが実際に高速であるかを確認するには、実際にベンチマークする価値があります。 2番目のクエリは(おそらく)クエリが少ないため、より良いように見えますが、これらのクエリはそれぞれm2m中間テーブルとの結合を実行する必要があります。前者の場合、結合クエリを1つだけ実行してから、単純な結合クエリを多数実行します。一部のデータベースバックエンドは、少数の複雑なクエリよりも多くの小さなクエリの方がパフォーマンスが良くなります。

2番目の方法がユースケースで実際に非常に高速であり、コードをクリーンアップするために追加の作業を実行する場合、汎用モデルのカスタムマネージャーメソッドを記述して、フェッチ"特定のクエリセットに関連する特定のテーブルからのすべてのサブタイプデータ。サブタイプテーブルごとに1つのクエリのみを使用します。 このスニペットが一括プリフェッチで一般的な外部キーを最適化する方法に似ています。これにより、最初のオプションのDRYer構文を使用して、2番目のオプションと同じクエリが提供されます。

他のヒント

完全な回答ではありませんが、これを行うことにより、多数のヒットを回避できます

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())

実際には、Pythonリストへのキャストを強制することにより、django ormにクエリセット内のすべての要素を強制的にロードさせます。次に、これを1つのクエリで実行します。

次の投稿で誤ってスタブリングしましたが、これはあなたの質問にほとんど答えています:

http://lazypython.blogspot.com/2008 /11/timeline-view-in-django.html

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top