我设法在以下模型之间建立多对多关系
现在,PlayerSkills有一个Skills通常不具备的属性:一个级别。
模型看起来像这样(为简洁而编辑):
class PlayerSkill < ActiveRecord::Base
belongs_to :character
belongs_to :skill
end
class Skill < ActiveRecord::Base
has_many :player_skills
has_many :characters, :through => :player_skills
attr_accessible :name, :description
end
class Character < ActiveRecord::Base
belongs_to :user
has_many :player_skills
has_many :skills, :through => :player_skills
end
所以在模特中没什么太奇特的...... 此时控制器也非常基本......这几乎是股票更新行动。
我想要修改的表单是字符#edit。 现在它呈现了一系列复选框,用于添加/删除角色的技能。 这很好,但使用has_many:through的全部意义在于跟踪“级别”。
这是我到目前为止所做的:
- 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
- @skills.each do |skill|
= check_box_tag "character[skill_ids][]", skill.id, @character.skills.include?(skill)
=h skill.name
%br
%p
= f.submit
在呈现“skill.name”之后,我需要它来打印更新player_skill的text_field。
问题当然是player_skill可能存在也可能不存在! (取决于加载表单时是否已勾选方框!)
从我读过的所有内容来看,has_many:通过很棒,因为它允许你将关系本身视为一个实体......但我完全不知道如何处理这种形式的实体。 / p>
一如既往,感谢您提前给予我的任何帮助!
答案 0 :(得分:1)
一旦我了解了嵌套属性,它实际上相对简单!
这是新角色模型!
class Character < ActiveRecord::Base
belongs_to :user
has_many :player_skills
has_many :skills, :through => :player_skills
accepts_nested_attributes_for :player_skills
def skills_pre_update(params)
skills = Skill.find(:all, :order => 'id')
skills = skills.map do |skill|
skill.id
end
self.skill_ids = []
self.skill_ids = skills
self.skill_ids.each_with_index do |skill_id, index|
self.player_skills[index].level = params[:character][:player_skills_attributes][index][:level]
end
self.skill_ids = params[:character][:skill_ids]
end
end
字符控制器的更新操作略有改变:
@character.skills_pre_update(params)
params[:character].delete(:player_skills_attributes)
params[:character].delete(:skill_ids)
原因是,这两个部分已经由pre_update操作处理,因此它们不需要再次由update_attributes处理,稍后会调用它们。
观点相对简单。 “多对多”复选框仍然相同,但我添加了新的文本框!
- @skills.each_with_index do |skill,index|
= check_box_tag "character[skill_ids][]", skill.id, @character.skills.include?(skill)
=h skill.name
-ps = skill.player_skills.find_by_character_id(@character) || skill.player_skills.build
-fields_for "character[player_skills_attributes][]", ps do |psf|
=psf.text_field(:level, :index => nil)
=psf.hidden_field(:id, :index => nil)
本质上,我必须在Characters模型中删除skill_ids(skill_ids = []
)的原因是因为否则它会不正确地设置顺序。
从本质上讲,我添加了所有技能 使用文本框更新级别 然后将技能重置为用户实际检查的内容(这将删除任何未使用的技能。)
我觉得这不是最好的解决方案 - 实际上对我来说感觉相当骇人。 因此,如果有其他人想要更好,更快/更优雅的解决方案,请随意!
否则,我希望这可以帮助其他人...因为修改连接表上的额外属性(不给连接表提供自己的控制器/视图)真是太痛苦了!
答案 1 :(得分:0)
我不确定答案,但这是我的想法:
对于控制器:
@character = Character.find(params[:id])
在视图中:
<% if @character.skills!=0 %>
<% for skill in @character.skills %>
<%=h skill.name %>
<%= check_box_tag(skill.name, value = "1", checked = false, options = {...}) %>
<% end %>
<% end %>
希望它会有所帮助!