Rails find_by_ sql не возвращает запрошенные столбцы из связанных таблиц / моделей - PullRequest
1 голос
/ 04 марта 2020

Вопрос: Почему p.name не возвращается?

sql = "
SELECT reports.id, reports.address, p.name 
FROM reports
JOIN places_reports pr ON reports.id = pr.report_id
JOIN places p ON pr.place_id = p.id
WHERE reports.id = 14
"

Report.find_by_sql(sql)

Возвращает:

#<Report id: 14, address: "New Gisborne, vic, Australia">, #<Report id: 14, address: "New Gisborne, vic, Australia">]

Документация немного расплывчата, но, похоже, подразумевает, что она должна быть возвращена как атрибут отчета.

https://apidock.com/rails/ActiveRecord/Base/find_by_sql/class

Ответы [ 3 ]

2 голосов
/ 04 марта 2020

Я поспешно прокомментировал, пока я не был рядом с компьютером, и упустил, что вы можете получить доступ к атрибутам, которые не принадлежат записи по записям, выбранным командой find по sql.

Вот другие варианты:

Если вам не нужна функциональность, определенная в вашей модели, вы можете получить необработанные данные, вернувшись к соединению базы данных и вызвав select_all, который возвращает массив хешей.

ActiveRecord::Base.connection.select_all(sql)
# [{'id': 1, 'address': '123 fake street', 'name': 'harry'}]

Вы можете также назвать эти атрибуты так, как вам нравится в самом SQL:

sql = <<-SQL
  SELECT reports.id report_id, reports.address, p.name place_name
  FROM reports
  JOIN places_reports pr ON reports.id = pr.report_id
  JOIN places p ON pr.place_id = p.id
  WHERE reports.id = 14
SQL

В качестве альтернативы, если вы хотите функциональность из модели (или обеих моделей) Вы можете включить их в свой выбор. например,

report = Report
  .includes(report_places: :places)
  .where(report_id: 14)
  .first
# Runs 3 SQL statements up-front (no matter how many records are found)
report.places
# No SQL staements run, array of Place model instances returned

Как указывает @ mu-is-too-short, вы можете просто получить атрибуты для экземпляров Report, но есть несколько причин, по которым я бы не стал этого делать .

  • Будет возвращено несколько экземпляров Report с одним и тем же идентификатором, но с другим именем.
  • Создается ложное впечатление, что name является атрибутом Report. Это может сбить с толку, когда вы вернетесь к коду.
1 голос
/ 04 марта 2020

Возвращаемое значение, которое вы видите:

#<Report id: 14, address: "New Gisborne, vic, Australia">, #<Report id: 14, address: "New Gisborne, vic, Australia">]

- это вывод #inspect, который включает только столбцы, о которых Report знает. Дополнительные значения из SQL все еще существуют и имеют методы доступа, так что вы можете сказать что-то вроде этого:

sql = %q(
  SELECT reports.id, reports.address, p.name 
  FROM reports
  JOIN places_reports pr ON reports.id = pr.report_id
  JOIN places p ON pr.place_id = p.id
  WHERE reports.id = 14
)
Report.find_by_sql(sql).each { |r| puts r.name }

, и это будет работать. Вы также можете добавить псевдонимы к SQL, чтобы получить «лучшие» имена методов:

sql = %q(
  SELECT reports.id, reports.address, p.name as place_name
  FROM reports
  JOIN places_reports pr ON reports.id = pr.report_id
  JOIN places p ON pr.place_id = p.id
  WHERE reports.id = 14
)
Report.find_by_sql(sql).each { |r| puts r.place_name }
1 голос
/ 04 марта 2020

Спасибо AJFaraday

sql = "
SELECT reports.id, reports.address, p.name 
FROM reports
JOIN places_reports pr ON reports.id = pr.report_id
JOIN places p ON pr.place_id = p.id
WHERE reports.id = 14
"

ActiveRecord::Base.connection.select_all(sql)
...