Django select_related 不显示所有字段,但原始 SQL 显示

Posted

技术标签:

【中文标题】Django select_related 不显示所有字段,但原始 SQL 显示【英文标题】:Django select_related doesn't show all fields, but raw SQL does 【发布时间】:2020-11-09 21:59:45 【问题描述】:

我有一个主机表和一个参数表,外键将参数链接回主机。我想选择所有的主机,以及参数“kernelversion”。

>>> q = Parameter.objects.filter(name__exact='kernelrelease').select_related('host')[:1]

这是它将使用的 SQL 查询,注意它选择了 inventory_host 中的所有字段,但最终的 QuerySet 中只显示了 inventory_parameter 列

>>> print(q.query)
SELECT `inventory_parameter`.`id`, `inventory_parameter`.`name`, `inventory_parameter`.`value`, `inventory_parameter`.`host_id`, `inventory_host`.`id`, `inventory_host`.`certname`, `inventory_host`.`report_timestamp`, `inventory_host`.`role`, `inventory_host`.`ipaddress`, `inventory_host`.`operatingsystemrelease`, `inventory_host`.`manufacturer`, `inventory_host`.`productname`, `inventory_host`.`alive`, `inventory_host`.`datacenter_id` FROM `inventory_parameter` INNER JOIN `inventory_host` ON (`inventory_parameter`.`host_id` = `inventory_host`.`id`) WHERE `inventory_parameter`.`name` = kernelrelease ORDER BY `inventory_parameter`.`name` ASC  LIMIT 1

>>> q.values()
<QuerySet ['id': 133376, 'name': 'kernelrelease', 'value': '2.6.32-754.3.5.el6.x86_64', 'host_id': 4061]>

我尝试手动运行上面的 SQL 查询,它提供了它应该提供的所有结果。为什么 django 从输出中删除所有 inventory_host 列?

MariaDB [opstools]> SELECT `inventory_parameter`.`id`, `inventory_parameter`.`name`, `inventory_parameter`.`value`, `inventory_parameter`.`host_id`, `inventory_host`.`id`, `inventory_host`.`certname`, `inventory_host`.`report_timestamp`, `inventory_host`.`role`, `inventory_host`.`ipaddress`, `inventory_host`.`operatingsystemrelease`, `inventory_host`.`manufacturer`, `inventory_host`.`productname`, `inventory_host`.`alive`, `inventory_host`.`datacenter_id` FROM `inventory_parameter` INNER JOIN `inventory_host` ON (`inventory_parameter`.`host_id` = `inventory_host`.`id`) WHERE `inventory_parameter`.`name` = 'kernelrelease' AND host_id = 4061 ORDER BY `inventory_parameter`.`name` ASC  LIMIT 1\G
*************************** 1. row ***************************
                    id: 133376
                  name: kernelrelease
                 value: 2.6.32-754.3.5.el6.x86_64
               host_id: 4061
                    id: 4061
              certname: myhost001.sub.domain.com
      report_timestamp: 2020-10-09 11:12:33.765000
                  role: qwerty
             ipaddress: 10.1.108.53
operatingsystemrelease: 6.10
          manufacturer: VMware, Inc.
           productname: VMware Virtual Platform
                 alive: 1
         datacenter_id: 3
1 row in set (0.00 sec)

【问题讨论】:

.select_related 项目将.values() 中。 .select_related 用于为 ForeignKey 填充对象。 OP,我建议阅读关于 select_related 的 django 文档;它没有用于您假设的用途,因此阅读文档将有助于澄清。 【参考方案1】:

似乎 Django 查询是正确的,所有数据都在那里,但 .values() 并没有全部显示出来。如果我完成了.values('value', 'host__certname'),它将显示两列,value 来自Parameters 表和certname 来自Host 表,因为那时我告诉 Django 给我来自 FK 相关表的数据。

正如我的问题中的人在他们的 RTFM 建议中暗示的那样,Django 自动遵循所有外键关系,所以如果我有 Parameter -&gt; Host -&gt; Datacenter,并且 FK 将它们链接到该链上,选择 Parameter 允许我也可以自动访问来自HostDatacenter 的值。但是,当调用这些值时,它会在后台执行更多的 SQL 查询,因此为了避免对每一行进行 SQL 查询,您可以select_related 在第一次查询时从那些 FK 关系中获取所有值,并且它运行很多更少的查询。

由于 Django 遵循 FK 关系的方式,如果该表位于像 Parameter -&gt; Host -&gt; Datacenter 这样的 FK 关系的“中间”,我的查询似乎无法以 Host 开头。为了从Host -&gt; Parameter 执行“反向”键,您需要_set,如此处所述 - reverse related object lookup

【讨论】:

以上是关于Django select_related 不显示所有字段,但原始 SQL 显示的主要内容,如果未能解决你的问题,请参考以下文章

Django查询优化之select_related和prefetch_related

pythonのdjango select_related 和 prefetch_related()

django: select_related() 在一个已经存在的对象上?

迭代 Django 中的相关对象:循环查询集或使用单行 select_related(或 prefetch_related)

Django 管理员内联:select_related

如何使用'select_related'从相关(ForeignKey)django模型中接收并非所有字段