私はいつも、Django orm のサブクラス化モデルの処理がかなり気の利いたものであることに気付きました。それがおそらく、このような問題に遭遇する理由です。
3 つのモデルを取り上げます。
class A(models.Model):
field1 = models.CharField(max_length=255)
class B(A):
fk_field = models.ForeignKey('C')
class C(models.Model):
field2 = models.CharField(max_length=255)
これで、モデルにクエリを実行して、可能な場合A
はすべてのモデルを取得B
できます。
the_as = A.objects.all()
for a in the_as:
print a.b.fk_field.field2 #Note that this throws an error if there is no B record
これに関する問題は、すべてのデータを取得するために膨大な数のデータベース呼び出しを見ていることです。
ここで、データベース内のすべてのモデルの QuerySet を取得する必要があるとしA
ます。ただし、すべてのサブクラス レコードとサブクラスの外部キー レコードも取得し、 を使用select_related()
してアプリケーションを単一のデータベース呼び出しに制限します。次のようなクエリを記述します。
the_as = A.objects.select_related("b", "b__fk_field").all()
1 つのクエリで、必要なすべてのデータが返されます。素晴らしい。
そうでないことを除いて。このバージョンのクエリは独自のフィルタリングを行っているため、select_related
結果をまったくフィルタリングすることは想定されていません。
set_1 = A.objects.select_related("b", "b__fk_field").all() #Only returns A objects with associated B objects
set_2 = A.objects.all() #Returns all A objects
len(set_1) > len(set_2) #Will always be False
django-debug-toolbar を使用してクエリを調べたところ、問題が見つかりました。生成された SQL クエリは、他のサブクラス化されたフィールドのように、 を使用してテーブルをクエリINNER JOIN
に結合します。C
LEFT OUTER JOIN
SELECT "app_a"."field1", "app_b"."fk_field_id", "app_c"."field2"
FROM "app_a"
LEFT OUTER JOIN "app_b" ON ("app_a"."id" = "app_b"."a_ptr_id")
INNER JOIN "app_c" ON ("app_b"."fk_field_id" = "app_c"."id");
そして、単純に を に変更すると、必要なレコードが得られるように見えINNER JOIN
ますLEFT OUTER JOIN
が、Django の ORM を使用している場合は役に立ちません。
select_related()
これは Django の ORM のバグですか? これを回避する方法はありますか? それとも、データベースに直接クエリを実行して結果を自分でマップする必要がありますか? これを行うには、Django-Polymorphic のようなものを使用する必要がありますか?