Rails,解决这个n + 1的技巧?

时间:2017-05-31 09:24:59

标签: ruby-on-rails activerecord

我有一个员工视图,其中列出了所有技能,这些技能写在我的数据库的技能表中。对于每个员工,所有技能都会显示出来,就像希望的那样。

员工和技能彼此相关,有很多:通过关联。

class Employee < ApplicationRecord
  has_many :employeeskillsets, foreign_key: "employee_id"
  has_many :skills, through: :employeeskillsets
end

class Skill < ApplicationRecord
  has_many :employeeskillsets, foreign_key: "skill_id"
  has_many :employees, through: :employeeskillsets
end 

class Employeeskillset < ApplicationRecord
  belongs_to :employee, foreign_key: 'employee_id'
  belongs_to :skill, foreign_key: 'skill_id'
end

所有这些技能都显示为按钮,可以切换以启用/禁用该员工的特定技能(每次点击直接插入/删除,无需额外提交)。

<%= link_to apply_skill_employee_path(employee: @employee, skill_id: skill.id), method: :put, remote: :true do %>
<%= skill.name %></div><% end %>

但是现在,我希望在加载页面时按钮显示为彩色。如果已启用该技能,则按钮颜色应为绿色,否则为灰色。这里开始我的问题: 我的应用程序使用一个单独的select语句检查每项技能我使用以下代码:

<%= link_to apply_skill_employee_path(employee: @employee, skill_id: skill.id), method: :put, remote: :true do %>
  <% if @employee.skills.exists?(skill.id) %>
    <div class="button skill e-true"><%= skill.name %></div>
  <% else %>
    <div class="button skill"><%= skill.name %></div>
  <% end %>

我已经尝试过使用包含,但似乎存在?独立检查每项技能。

这里有人有一个建议,我怎么能用一个选择来解决这个问题?

在此先感谢,我希望我已经提到了所有必要的内容。

编辑1:我忘了提及,我通过部分渲染(如果知道这很重要)。 这里是当前在employees_controller中使用的@employee var。

@employee = Employee.find_by(id: params[:id])

4 个答案:

答案 0 :(得分:0)

由于skill也是一个有效的记录模型,您可以在员工的技能集合上使用include?来检查员工是否具有特定的技能。

@employee.skills.include?(skill)

通过这种方式,您可以自由地使用includes条款来急切地加载员工的技能。

答案 1 :(得分:0)

尝试pluck ing ID,然后检查include?,因为您不需要获取skills的所有属性

<% skills = @employee.skills.pluck(:id) %>
<%= link_to apply_skill_employee_path(employee: @employee, skill_id: skill.id), method: :put, remote: :true do %>
<% if skills.include?(skill.id) %>
  <div class="button skill e-true"><%= skill.name %></div>
<% else %>
  <div class="button skill"><%= skill.name %></div>
<% end %>

答案 2 :(得分:0)

这不会触发附加查询

<% if @employee.skill_ids.exists?(skill.id) %>

还要避免以下行中的n+1

apply_skill_employee_path(employee: @employee, skill_id: skill.id)

确保包含skills

@employee = Employee.includes(:skills).where(......)

答案 3 :(得分:0)

Rails方式简单得多。

当您在ActiveRecord中使用has_many宏时,它还会创建一个_ids方法,可用于添加或删除与数组的关系:

@employee.skills_ids = [1,2,3]

这也适用于与:through选项的间接关联。

您可以将它与表单集助手一起使用来创建select或checkbox标签:

<%= form_for(@employee) do |f| %>
  <%= f.label :skill_ids, 'Skills' %>
  <%= f.collection_check_boxes(:skills_ids, Skill.all, :id, :name) %>
<% end %>

为了避免额外的查询,您可以在控制器中执行左外连接:

def edit
  # left_outer_joins is new in Rails 5
  # see https://blog.bigbinary.com/2016/03/24/support-for-left-outer-joins-in-rails-5.html
  @employee.left_outer_joins(:skills).find(params[:id])
end

您也不需要在控制器中使用愚蠢的额外方法来处理应作为正常更新处理的内容。 KISS。