Rails - 关于“N + 1”的更多信息

时间:2017-03-01 23:12:15

标签: ruby-on-rails activerecord

当我尝试在索引操作中显示链接数据时 - 我无法避免使用标准包含的N + 1问题。例如:

Model 1 - Pet (let it will be animal: title:string. Pet can has many PetAttributeElems, they show attributes this Pet have and what values they have)
Model 2 - PetAttribute (this model contains only titles of attributes, like  weight, age and 1000+ more attributes. PetAttribute has many PetAttributeElems - one attribute, such as weight, can be described for many Pets)
Model 3 - PetAttributeElem (this model belongs to pet and to petAttribute, also it has value field, that show value of attribute for pet.

当我做show动作时 - 我在HAML中使用:

-@pet.pet_attribute_elems.includes(:pet_attribute).each do |elem|
      ="#{elem.pet_attribtute.title}: #{elem.value}"

但是当我做index动作时,我想使用:

-@pets.each do |pet|
  -pet.pet_attribute_elems.includes(:pet_attribute).each do |elem|
     ="#{elem.pet_attribtute.title}: #{elem.value}"

对于每个includes

pet方法将调用许多SQL查询

现在我通过手动创建这样的附加对象来解决它:

@pet_elems = {}
PetAtributeElems.includes(:pet_attribute)
  .where(pet_id:@pets.map(&:id)).each do |elem|
  pet_id = elem.pet_id
  if @pet_elems.include?(pet_id)
    @pet_elems[pet_id] << elem
  else
    @pet_elems[pet_id] = [elem]
  end
end

我可以使用:

-@pets.each do |pet|
  -if @pet_elems.include?(pet.id)
    -@pet_elems[pet.id].each do |elem|
      ="#{elem.pet_attribtute.title}: #{elem.value}"

我怎样才能更轻松地解决这个问题?

1 个答案:

答案 0 :(得分:1)

你正沿着非rails-convention路径前进。

将代码移出视图,因此只需

= render @pet_attribute_elems

部分处理显示

# _pet_attribute_elems.html.haml
="#{pet_attribute_elem.pet_attribtute.title}: #{pet_attribute_elem.value}"

在控制器中,执行查询

def show

  @pet = Pet.find(...)
  @pet_attribute_elems = @pet.pet_attribute_elems.includes(:pet_attribute)

end

def index

  @pet_attribute_elems = PetAttributeElem.includes(:pet_attribute)

end