可以急切加载与nested_attributes的关联吗?
简而言之,我遇到了可怕的 2(n) 查询问题。 如果 n = 数据库中的技能数量,那么我的characters#edit 表单将需要 2(n) 次查询来加载页面。每个技能都会选择一次 PlayerSkill(连接表),并且每个技能都会查找一次技能。
这是一些我认为与情况相关的代码。本质上,这个过程中涉及的模型、视图和控制器,更少的模型验证和更少的我不关心的操作。
控制器:
# GET /characters/1/edit
def edit
@character = Character.find(params[:id], :include => {:player_skills => :skill})
stub_player_skills
end
private
def stub_player_skills
@skills = Skill.find(:all)
@skills.each do |skill|
if (skill.player_skills.empty?)
ps = @character.player_skills.build(:skill_id => skill.id, :name => skill.name)
end
end
end
模型:
class Character < ActiveRecord::Base
belongs_to :user
belongs_to :campaign
has_many :sheets, :dependent => :destroy
has_many :tokens, :dependent => :destroy
has_many :player_skills, :dependent => :destroy
has_many :skills, :through => :player_skills
accepts_nested_attributes_for :player_skills, :allow_destroy => true
end
有问题的视图(HAML):
%h1
Editing Character
- form_for @character do |f|
= f.error_messages
%p
= f.label :name
%br
= f.text_field :name
%p
= f.label :race
%br
= f.text_field :race
%p
= f.label :char_class
%br
= f.text_field :char_class
%p
-f.fields_for :player_skills do |ps|
=ps.object.skill.name
=ps.text_field :level
=ps.hidden_field :skill_id
-unless ps.object.new_record?
=ps.check_box '_destroy'
=ps.label '_destroy', 'Remove'
%br
%p
= f.submit
我对这种情况的理解是,急切加载的存在是为了在(大致)单个额外查询中获取关联。
我需要在两个区域正确应用急切加载,但我只是不知道如何做到这一点。
在stub_player_skills方法中,它需要创建一个PlayerSkill对象假设角色还没有。 它可以从这里的预先加载中受益,因为它循环遍历数据库中的每项技能。这就是第一个“n 查询”的来源。
然后在视图上, fields_for 循环遍历我们已经积累的所有 PlayerSkills,因为这里无法立即加载,当我调用 =ps.object.skill.name 打印出技能名称时,它会进行技能查找,它引入了第二组“n 查询”。
我主要关心的是视图层,我找不到任何文档(Rails API 或其他)来说明如果您使用 fields_for 生成嵌套表单,如何立即加载关联。
感谢您的所有回复:) 〜罗比
Just briefly, I have run into a dreaded 2(n) queries problem.
If n = the number of skills in the database, then my characters#edit form will take 2(n) queries to load the page. It will SELECT a PlayerSkill (the join table) once every skill, and it will look up the Skill once per skill.
Here is some code which I believe is pertinent to the situation. In essence, the models, views, and controllers involved in this process, less the model validations and less the actions I'm not concerned about.
The controller:
# GET /characters/1/edit
def edit
@character = Character.find(params[:id], :include => {:player_skills => :skill})
stub_player_skills
end
private
def stub_player_skills
@skills = Skill.find(:all)
@skills.each do |skill|
if (skill.player_skills.empty?)
ps = @character.player_skills.build(:skill_id => skill.id, :name => skill.name)
end
end
end
The model(s):
class Character < ActiveRecord::Base
belongs_to :user
belongs_to :campaign
has_many :sheets, :dependent => :destroy
has_many :tokens, :dependent => :destroy
has_many :player_skills, :dependent => :destroy
has_many :skills, :through => :player_skills
accepts_nested_attributes_for :player_skills, :allow_destroy => true
end
The offending view (HAML):
%h1
Editing Character
- form_for @character do |f|
= f.error_messages
%p
= f.label :name
%br
= f.text_field :name
%p
= f.label :race
%br
= f.text_field :race
%p
= f.label :char_class
%br
= f.text_field :char_class
%p
-f.fields_for :player_skills do |ps|
=ps.object.skill.name
=ps.text_field :level
=ps.hidden_field :skill_id
-unless ps.object.new_record?
=ps.check_box '_destroy'
=ps.label '_destroy', 'Remove'
%br
%p
= f.submit
My understanding of the situation is that eager loading exists to grab the association in (roughly) a single extra query.
I need to properly apply eager loading in two areas, and I am just at a loss regarding how to do it.
In the stub_player_skills method, it needs to create a PlayerSkill object assuming the character does not already have one.
It could benefit from eager loading here because it loops through each skill in the database. This is where the first "n-queries" are coming from.
Then on the view, fields_for loops through all the PlayerSkills we've racked up, because there is no way to eager load here, when I call =ps.object.skill.name to print out the skill's name, it does a Skill lookup, which brings in the second set of "n-queries."
My primary concern lies in the view layer, I cannot find any documentation (Rails API or otherwise) that states how you could eager load the associations if you're using fields_for to generate a nested form.
Thanks for any and all responses :)
~Robbie
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你能试试这个并看看它是否有效吗?
您可以保持模型不变。
你的控制器看起来像这样
Can you try this and see if it will work?
You can keep your model the way it is.
Your controller can then look like this
如果有人对这个问题的“最终”解决方案感兴趣:
我已经诉诸于存储技能名称数组,并通过计数器在视图中引用它,如下所示:
在控制器中,我几乎移动了Stub_player_skills 方法所属的所有逻辑,并从 Coderama 的书中取出一页,我想出了这个:
在模型层中,我只需
:include =>; has_many :through 关系上的 :skill
可以消除更多查询。In case anyones interested in my "final" solution to this problem:
I've resorted to storing an array of the skill names, and referencing it in the view via a counter, as seen here:
In the controller, I've moved almost all the logic to the stub_player_skills method where it belongs, and taking a page out of Coderama's book, I've come up with this:
In the model layer, I just had to
:include => :skill
on the has_many :through relationship to get rid of a few more queries.