Django select_related не показывает все поля, но raw SQL показывает

#django #join #orm

#django #Присоединиться #orm

Вопрос:

У меня есть таблица узлов и таблица параметров с внешним ключом, связывающим параметры с узлами. Я хочу выбрать все хосты и параметр «kernelversion».

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

Это SQL-запрос, который, как он показывает, будет использоваться, обратите внимание, что он выбирает все поля из inventory_host, однако в конечном наборе запросов отображаются только столбцы 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 1G
*************************** 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)
  

Комментарии:

1. .select_related элементы не будут .values() включены. .select_related используется для заполнения объектов для a ForeignKey .

2. OP, я рекомендую прочитать документацию django по select_related; он не используется для того, для чего, как вы предполагаете, он используется, поэтому чтение документов поможет прояснить ситуацию.

3. Может кто-нибудь, пожалуйста, помогите мне указать правильный путь для того, какой запрос ORM выдает мне все хосты из таблицы hosts и их значение из таблицы параметров, где «parameter.name = kernelversion»

Ответ №1:

Кажется, запрос Django был правильным, и все данные были там, но .values() не отображали все это. Если бы я это сделал .values('value', 'host__certname') , это показало бы 2 столбца, value из Parameters таблицы и certname из Host таблицы, потому что тогда я говорю Django предоставить мне данные из связанной с FK таблицы.

Как намекнули люди в комментариях к моему вопросу в своих предложениях RTFM, Django автоматически отслеживает все отношения с внешним ключом, поэтому, если у меня есть Parameter -> Host -> Datacenter , с FK, связывающий их с этой цепочкой, выбор a Parameter позволяет мне Host также Datacenter автоматически получать доступ к значениям из и. Однако при вызове этих значений он выполняет больше SQL-запросов за кулисами, поэтому, чтобы избежать выполнения SQL-запроса для каждой строки, вы можете select_related получить все значения из этих отношений FK в первом запросе, и он выполняет гораздо меньше запросов.

В результате того, как Django следует за отношениями FK, кажется, что мой запрос не может начинаться с Host, если эта таблица находится в «середине» отношения FK, например Parameter -> Host -> Datacenter . Для того, чтобы выполнить «обратный» ключ из Host -> Parameter , вам понадобится _set , как описано здесь, — обратный поиск связанных объектов