我的rails 4应用程序有许多Measures
属于Station
。
我试图在我的控制器中加载措施:
@station = Station.includes(:measures).friendly.find(params[:id])
@measures = @station.measures
但是,当我向测量模型添加一个访问工作站属性的方法时,每个小节会产生一个额外的查询:
SELECT "stations".* FROM "stations" WHERE "stations"."id" = ? ORDER BY "stations"."id" ASC LIMIT 1
这意味着每页加载大约50个查询,这完全可以提高性能。
如何正确加载关系以避免此n + 1查询?我错了吗?
github: /app/controllers/stations_controller.rb
class StationsController < ApplicationController
...
# GET /stations/1
# GET /stations/1.json
def show
@station = Station.includes(:measures).friendly.find(params[:id])
@measures = @station.measures
end
...
end
github: /app/models/measure.rb
class Measure < ActiveRecord::Base
belongs_to :station, dependent: :destroy, inverse_of: :measures
after_save :calibrate!
after_initialize :calibrate_on_load
...
def calibrate!
# this causes the n+1 query
unless self.calibrated
unless self.station.speed_calibration.nil?
self.speed = (self.speed * self.station.speed_calibration).round(1)
self.min_wind_speed = (self.min_wind_speed * self.station.speed_calibration).round(1)
self.max_wind_speed = (self.max_wind_speed * self.station.speed_calibration).round(1)
self.calibrated = true
end
end
end
def calibrate_on_load
unless self.new_record?
self.calibrate!
end
end
def measure_cannot_be_calibrated
if self.calibrated
errors.add(:speed_calbration, "Calibrated measures cannot be saved!")
end
end
end
github: /app/models/stations.rb
class Station < ActiveRecord::Base
# relations
belongs_to :user, inverse_of: :stations
has_many :measures, inverse_of: :station, counter_cache: true
# slugging
extend FriendlyId
friendly_id :name, :use => [:slugged, :history]
...
end
ADDITION 有趣的是,这不会导致n + 1查询。但我宁愿不在我的控制器上复制它。
class Measure < ActiveRecord::Base
...
# after_initialize :calibrate_on_load
...
end
@station = Station.includes(:measures).friendly.find(params[:id])
@measures = @station.measures
@measures.each do |m|
m.calibrate!
end
答案 0 :(得分:0)
尝试将station
包括在measure
中。
def show
@station = Station.includes(:measures).friendly.find(params[:id])
@measures = @station.measures.includes(:station)
end
答案 1 :(得分:0)
:includes
选项通常会在您看到的单独查询中触发加载,因为在许多情况下它更具性能。如果您想确保在一个查询中完成,请尝试使用:joins
,但要注意返回的结果记录将是只读的。
答案 2 :(得分:0)
after_initialize
发生在关系由连接或包含加载之前。见https://github.com/rails/rails/issues/13156
我决定在使用不同的方法后提出一些好的建议,然后想出来:
class Station < ActiveRecord::Base
...
alias_method :measures_orig, :measures
def measures
measures_orig.map do |m|
m.calibrate!
end
end
end