【问题标题】:Django: select_related with multi table inheritanceDjango:select_related 与多表继承
【发布时间】:2018-11-16 15:55:32
【问题描述】:

我的模型结构如下:

class A(models.Model):
    prop_a = models.CharField(max_length=255)

class B(A):
    prop_b = models.CharField(max_length=255)

class C(A):
    prop_c = models.CharField(max_length=255)

class D(models.Model):
    fk = models.ForeignKey('A')

所以本质上,我有一个模型 (D),它有一个“抽象”模型 (A) 的外键,它是 BC 的子类。

现在,当我运行D.objects.all().select_related() 时,只查询A 的属性。我认为这是因为在查询时,Django 不知道 fk 是哪个子类的实例(在我当前的结构中也不知道)。

有什么方法可以在不改变模型结构的情况下查询子类的属性?

我还尝试了 prefetch_related 并尝试使用来自 django-model-utils 的 InheritanceManager,但均无济于事。

编辑:澄清一下,我正在寻找一种方法来遍历D 的对象,访问fk 的具体值(BC 对象) 而只访问数据库一次。

运行 D.objects.all().select_related() 会产生查询

'SELECT "mapping_d"."id", "mapping_d"."fk_id", "mapping_a"."id", "mapping_a"."prop_a" FROM "mapping_d" INNER JOIN "mapping_a" ON ("mapping_d"."fk_id" = "mapping_a"."id")'

假设其中一个是D 类型的对象d,其属性fk 的类型为C

现在,当我访问值 d.fk.c 时,Django 运行一个额外的查询来获取 C 的属性:

'SELECT "mapping_a"."id", "mapping_a"."prop_a", "mapping_c"."a_ptr_id", "mapping_c"."prop_c" FROM "mapping_c" INNER JOIN "mapping_a" ON ("mapping_c"."a_ptr_id" = "mapping_a"."id") WHERE "mapping_c"."a_ptr_id" = 3'

我想避免每个 D 对象的额外查询。以建议的方式使用 InheritanceManager 似乎没有这样做。

【问题讨论】:

    标签: python django inheritance


    【解决方案1】:

    我通常使用Inheritance Manager 并按预期运行。

    1) 记得在A上设置继承管理器:

    class A(models.Model):
        # ...
        objects = InheritanceManager()
    

    2) 进行正确的查询:

    some_d_object = D.objects.get( pk = 1 )
    related = some_d_object.fk.all().select_related()
    

    请注意,您的查询 D.objects.all().select_related() 将返回 D 对象。没有人从D 继承,那么就没有子类,只有D 对象。

    3) 享受继承。

    已编辑我编辑此问题是因为 OP 评论。

    使用这种模式,当我访问相关对象时,Django 会执行额外的数据库查询。由于我有很多 D 对象,这变得相当昂贵。你知道有没有办法一次把它们全部取出来? ——

    好的,让我们试着写一个答案,因为我真的不完全理解 OP 正在寻找的女巫对象。问题很好,因为他几乎写了a Minimal, Complete, and Verifiable example,但我不知道预期值,让我们尝试一些示例。

    1) 如果你想要所有对象,那么简单:

    all_objects = A.objects.all().select_subclasses()
    

    2) 如果你想过滤,那么就去做吧。我写了一个示例:

    filtered_objects = A.objects.filter( d__pk = 1 ).select_subclasses()
    

    3) 在单个查询中获取所有子类以及 D 类:

    不幸的是你不能申请prefetch_related(也许你可以,但我不知道该怎么做)。然后你需要用 itertools 按摩数据。

    all_objects_and_D = list(
                         A
                         .objects
                         .select_related('D')
                         .select_subclasses()
                         .order_by( 'D__pk' )
                         )
    #at this point use itertools to massage your data
    keyfunc = lambda x: x.D
    data = [  { k, list(g) } 
              for k, g in 
              itertools.groupby(all_objects_and_D, key=keyfunc )
           ]
    

    【讨论】:

    • 使用这种模式,当我访问相关对象时,Django 会执行额外的数据库查询。由于我有很多 D 对象,这变得相当昂贵。你知道有什么方法可以一次性获取吗?
    • 感谢您抽出宝贵时间。我想我有点不清楚我到底想做什么,我相应地更新了问题。很抱歉造成混乱。
    • @Silvester,我的错。我混合了select_subclassesselect_related。现在我已经修好了。对不起。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 2010-11-26
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    • 2017-10-14
    相关资源
    最近更新 更多