rails activerecord关联连接表

时间:2014-10-21 12:20:13

标签: ruby-on-rails activerecord

我有一个国家模型和一个旅行笔记模型。一个国家有许多旅行记录,旅行记录属于一个国家。

class TravelNote < ActiveRecord::Base
  default_scope { order(created_at: :desc) }

  belongs_to  :country
  has_many    :chapters

  before_destroy { draft? }

  validate :validates_uniqueness_of_draft, on: :create

  enum status: { draft: 0, published: 1, archived: 2 }
  enum advice_against: { no: 0, general: 1, tourists: 2 }

  scope :country, ->(country_id) { where(country_id: country_id) }

  # further methods omitted...
end

class Country < ActiveRecord::Base
  default_scope { order(:iso_code) }

  has_many :travel_notes

end
app / controllers / countries_controller.rb中的

class CountriesController < ApplicationController
  def index
    @countries = Country.includes(:travel_notes)
  end
  # rest of the class omitted...
end

在app / views / countries / index.html.haml中:

@countries.each do |country|
        %tr
          %td= link_to country.name_de, country_travel_notes_path(country)
          %td= TravelNote.published.country(country.id).first.try :published_at

由于性能原因,我想删除TravelNote.published.country(country.id).first.try:published_at,以便不再有数百个数据库查询,而只是一个等效的SQL查询数组:

select * from countries 
left join travel_notes
on countries.id=travel_notes.country_id

我怎样才能实现它?

2 个答案:

答案 0 :(得分:1)

您可以编写自定义范围,仅包含已发布的笔记。

类似

scope :include_published, -> { proc do |_|
    joins("LEFT OUTER JOIN (
        SELECT b.*
        FROM   travel_notes b
        WHERE published = 1
        GROUP BY b.country_id
      ) notes_select ON notes_select.country_id = countries.id"
     ).select("countries.*").select("#{insert what attributes you want to include }")
  end.call(self, counted_model) }

您必须在第二个选择子句中包含所需的属性,然后它们将作为具有相同名称的方法包含在国家/地区活动记录结果中。

SQL查询可以写得更漂亮,我只是把东西放在一起......

我在项目中使用了类似的技术,但是为了包含相关对象的计数。

答案 1 :(得分:1)

显然,您正试图加载与该国家相关的“travel_notes”:

Country.includes(:travel_notes).where(travel_notes: { status: 1} )

所以你的代码将是:

class CountriesController < ApplicationController
  def index
    @countries = Country.includes(:travel_notes).where(travel_notes: { status: 1} )
  end
  # rest of the class omitted...
end
@countries.each do |country|
        %tr
          %td= link_to country.name_de, country_travel_notes_path(country)
          %td= country.travel_notes.first.published_at