【问题标题】:Rails 5 - n+1 query issue inside simple_form gemRails 5 - simple_form gem 中的 n+1 查询问题
【发布时间】:2017-12-17 15:29:23
【问题描述】:

我正在尝试解决 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.erbedit.html.erb。问题发生在我试图获取 room.building.title 的房间关联上。

截图如下:

如您所见,我只想显示房间名称及其中心名称,但 Bullet 会引发错误。问题是我正在使用该 lambda 函数,因此在控制器中创建变量没有帮助(或者至少我不知道如何正确创建它),因为问题发生在选择框内。 在这里我包括我的控制器:

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

(在这种情况下与我们相关的操作是编辑

以下是相关模型:

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

很抱歉这篇长篇文章和我缺乏知识。如有任何反馈,我将不胜感激。

【问题讨论】:

  • 能否尝试在f.association中指定collection选项,然后使用includes来避免N+1查询问题?我的意思是这样的f.association :room, :label_method =&gt; lambda { |room| "#{room.title} (#{room.building.title})", :collection =&gt; Room.includes(:building).all }
  • 是的,解决了这个错误:)我不知道可以这样解决,非常感谢。 (我不知道如何将您的评论标记为解决方案,所以我只是投票)
  • 酷,我会把它作为答案:)

标签: ruby-on-rails ruby-on-rails-5 simple-form


【解决方案1】:

您可以使用f.association 中的includes 指定collection 选项。像这样:

f.association :room, :label_method =&gt; lambda { |room| "#{room.title} (#{room.building.title})", :collection =&gt; Room.includes(:building).all }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-02
    • 2011-09-24
    • 1970-01-01
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多