Rails 5 - simple_form gem 内的 n+1 个查询问题
Rails 5 - n+1 query issue inside simple_form gem
我正在尝试解决 Rails 中 Bullet gem 的所有 n+1 个问题,但我无法解决这个特定问题:
_form.html.erb
<%= simple_form_for([:admin, @course, @lesson]) do |f| %>
<div class="form-inputs">
<div class="col-md-6">
<%= f.input :start_at, minute_step: 5, start_year: Date.today.year %>
</div>
<div class="col-md-6">
<%= f.input :end_at, minute_step: 5, start_year: Date.today.year %>
</div>
<div class="col-md-6">
<!--Here is the problem-->
<%= f.association :room, :label_method => lambda { |room| "#{room.title} (#{room.building.title})"}%>
<!--Here is the problem-->
</div>
<div class="col-md-6">
<%= f.association :teacher, :label_method => lambda { |teacher| "#{teacher.first_name} #{teacher.last_name}"},
collection: @course.teachers%>
</div>
</div>
<div class="col-md-12">
<div class="form-actions text-center">
<%= f.submit class: "btn btn-primary btn-lg" %>
</div>
</div>
<% end %>
表单呈现为 new.html.erb 和 edit.html.erb。问题发生在我试图获得 room.building.title.
的房间关联上
截图如下:
如您所见,我只想显示房间名称及其中心名称,但 Bullet 抛出错误。问题是我正在使用该 lambda 函数,因此在控制器中创建变量没有帮助(或者至少我不知道如何正确创建它),因为问题发生在 select 框中。
这里我包括我的控制器:
lessons_controller.rb
module Admin
class LessonsController < Admin::AdminController
helper_method :convert_time, :convert_day
before_action :set_course
before_action :set_lesson, only: [:show, :edit, :update, :destroy]
def index
@lessons = Lesson.includes(:teacher,:course, :room => :building).where(course_id: @course).paginate(page: params[:page], per_page: 10)
# @course_lessons = @course.lessons.paginate(page: params[:page], per_page: 10)
end
def new
#@room = Room.all.includes(:building)
@lesson = Lesson.new
end
def create
@lesson = Lesson.new(lesson_params)
@lesson.course = @course
if @lesson.save
flash[:success] = "Lesson was created"
redirect_to admin_course_lessons_path(@course)
else
render 'new'
end
end
def edit
end
def update
if @lesson.update(lesson_params)
flash[:success] = "Lesson was updated"
redirect_to admin_course_lessons_path(@course)
else
render 'edit'
end
end
def show
end
def destroy
@lesson.destroy
flash[:danger] = "Lesson was deleted"
redirect_to admin_course_lessons_path(@course)
end
private
def set_course
@course = Course.find(params[:course_id])
end
def set_lesson
@lesson = Lesson.find(params[:id])
end
def lesson_params
params.require(:lesson).permit(:start_at,:end_at, :room_id, :teacher_id)
end
end
end
(在这种情况下与我们相关的操作是 new 和 edit)
相关型号如下:
room.rb
class Room < ApplicationRecord
belongs_to :building
has_many :lessons, dependent: :destroy
before_save {self.title = title.upcase_first}
before_save {self.code = code.upcase}
validates :title, presence: true,
length: { minimum: 3, maximum: 50},
uniqueness: {case_sensitive: false}
validates :code, presence: true,
length: {minimum: 2},
uniqueness: {case_sensitive: false}
validates :building_id, presence: true
end
building.rb
class Building < ApplicationRecord
has_many :rooms, dependent: :destroy
before_save {self.title = title.upcase_first}
before_save {self.code = code.upcase}
validates :title, presence: true,
length: { minimum: 3, maximum: 50},
uniqueness: true
validates :code, presence: true,
length: {minimum: 3},
uniqueness: {case_sensitive: false}
end
抱歉这么长 post 和我缺乏知识。我将不胜感激任何反馈。
您可以在 f.association
中使用 includes
指定 collection
选项。像这样:
f.association :room, :label_method => lambda { |room| "#{room.title} (#{room.building.title})", :collection => Room.includes(:building).all }
我正在尝试解决 Rails 中 Bullet gem 的所有 n+1 个问题,但我无法解决这个特定问题:
_form.html.erb
<%= simple_form_for([:admin, @course, @lesson]) do |f| %>
<div class="form-inputs">
<div class="col-md-6">
<%= f.input :start_at, minute_step: 5, start_year: Date.today.year %>
</div>
<div class="col-md-6">
<%= f.input :end_at, minute_step: 5, start_year: Date.today.year %>
</div>
<div class="col-md-6">
<!--Here is the problem-->
<%= f.association :room, :label_method => lambda { |room| "#{room.title} (#{room.building.title})"}%>
<!--Here is the problem-->
</div>
<div class="col-md-6">
<%= f.association :teacher, :label_method => lambda { |teacher| "#{teacher.first_name} #{teacher.last_name}"},
collection: @course.teachers%>
</div>
</div>
<div class="col-md-12">
<div class="form-actions text-center">
<%= f.submit class: "btn btn-primary btn-lg" %>
</div>
</div>
<% end %>
表单呈现为 new.html.erb 和 edit.html.erb。问题发生在我试图获得 room.building.title.
的房间关联上截图如下:
如您所见,我只想显示房间名称及其中心名称,但 Bullet 抛出错误。问题是我正在使用该 lambda 函数,因此在控制器中创建变量没有帮助(或者至少我不知道如何正确创建它),因为问题发生在 select 框中。 这里我包括我的控制器:
lessons_controller.rb
module Admin
class LessonsController < Admin::AdminController
helper_method :convert_time, :convert_day
before_action :set_course
before_action :set_lesson, only: [:show, :edit, :update, :destroy]
def index
@lessons = Lesson.includes(:teacher,:course, :room => :building).where(course_id: @course).paginate(page: params[:page], per_page: 10)
# @course_lessons = @course.lessons.paginate(page: params[:page], per_page: 10)
end
def new
#@room = Room.all.includes(:building)
@lesson = Lesson.new
end
def create
@lesson = Lesson.new(lesson_params)
@lesson.course = @course
if @lesson.save
flash[:success] = "Lesson was created"
redirect_to admin_course_lessons_path(@course)
else
render 'new'
end
end
def edit
end
def update
if @lesson.update(lesson_params)
flash[:success] = "Lesson was updated"
redirect_to admin_course_lessons_path(@course)
else
render 'edit'
end
end
def show
end
def destroy
@lesson.destroy
flash[:danger] = "Lesson was deleted"
redirect_to admin_course_lessons_path(@course)
end
private
def set_course
@course = Course.find(params[:course_id])
end
def set_lesson
@lesson = Lesson.find(params[:id])
end
def lesson_params
params.require(:lesson).permit(:start_at,:end_at, :room_id, :teacher_id)
end
end
end
(在这种情况下与我们相关的操作是 new 和 edit)
相关型号如下:
room.rb
class Room < ApplicationRecord
belongs_to :building
has_many :lessons, dependent: :destroy
before_save {self.title = title.upcase_first}
before_save {self.code = code.upcase}
validates :title, presence: true,
length: { minimum: 3, maximum: 50},
uniqueness: {case_sensitive: false}
validates :code, presence: true,
length: {minimum: 2},
uniqueness: {case_sensitive: false}
validates :building_id, presence: true
end
building.rb
class Building < ApplicationRecord
has_many :rooms, dependent: :destroy
before_save {self.title = title.upcase_first}
before_save {self.code = code.upcase}
validates :title, presence: true,
length: { minimum: 3, maximum: 50},
uniqueness: true
validates :code, presence: true,
length: {minimum: 3},
uniqueness: {case_sensitive: false}
end
抱歉这么长 post 和我缺乏知识。我将不胜感激任何反馈。
您可以在 f.association
中使用 includes
指定 collection
选项。像这样:
f.association :room, :label_method => lambda { |room| "#{room.title} (#{room.building.title})", :collection => Room.includes(:building).all }