Rails, tips for solving this n+1?

不问归期 提交于 2019-12-12 05:38:00

问题


I have an employee view, where are listed all skills, which are written in the skills table on my db. For every employee, all skills are displayed, just as want it to.

Employees and skills are related to each other as has many :through association.

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

All those skills are displayed as buttons, which are toggles to enable/disable that particular skill on that employee (per click a direct insert/delete, no extra commit needed).

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

But now, I want to the buttons to be shown colored, when you load the page. If the skill is already enabled, the buttoncolor should be green, else grey. And here begins my problem: My app checks each skill with one separate select statement. I use the following code for this:

<%= 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 %>

I have already tried to use includes, but it seems, the exists? checks each skill independently.

Does anybody here have a suggestion, how I could solve this, by using one single select?

Thanks in advance, I hope I have mentioned everything, what is necessary.

Edit 1: I forgot to mention, that i render this through a partial (if that is important to know). And here is the current used @employee var in the employees_controller.

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

回答1:


Try plucking ids, and then check for include? as you don't need to fetch all attributes of 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:


Since skill is also an active record model, you can use include? on the employee's skills collection to check if the employee has a particular skill.

@employee.skills.include?(skill)

This way you are free to use includes clause to eagerly load employees' skills.




回答3:


This will not fire the additional query

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

Also to avoid n+1 in the following line

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

Make sure you are including skills

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



回答4:


The Rails way is far simpler.

When you use the has_many macro in ActiveRecord it also creates a _ids method that can be used to add or remove relations with an array:

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

This also works with indirect assocations with the :through option.

You can use this together with the form collection helpers to create select or checkbox tags:

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

To avoid an extra query you can do a left outer join in the controller:

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

You also don't need a silly extra method in your controller for something that should be handled as a normal update. KISS.



来源:https://stackoverflow.com/questions/44280787/rails-tips-for-solving-this-n1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!