django orm – 如何在超类的子类的外键上使用select_related()

我总是发现Django orm处理子类化模型非常漂亮.这可能就是我遇到像这样的问题的原因.

拿三种型号:

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

这样做的问题在于您正在查看大量数据库调用以检索所有数据.

现在假设您想要检索数据库中所有A模型的QuerySet,但是同时使用所有子类记录和子类的外键记录,使用select_related()将应用程序限制为单个数据库调用.你会写一个这样的查询:

the_as = A.objects.select_related("b", "b__fk_field").all()

一个查询返回所需的所有数据!真棒.

除外.因为这个版本的查询正在进行自己的过滤,即使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没有帮助.

这是Django的ORM中select_related()中的错误吗?有没有解决这个问题,或者我只是要直接查询数据库并自己映射结果?我应该使用像Django-Polymorphic这样的东西吗?

最佳答案 它看起来像一个bug,特别是它似乎忽略了A-> B关系的可空性质,例如,如果你在A中有一个外键引用B而不是子类,那么外键当然可以为空和django将使用左连接.你应该在django问题跟踪器中提出这个问题.您也可以尝试使用prefetch_related而不是select_related来解决您的问题.

点赞