我的问题是我遇到了accep_nested_attributes_for的局限性,所以我需要弄清楚如何自己复制这个功能以获得更大的灵活性。 (请参阅下面的内容,确切地说是什么让我心烦意乱。)所以我的问题是:如果我想要模仿并增强accept_nested_attributes_for,我的表单,控制器和模型应该是什么样子?真正的技巧是我需要能够使用现有的关联/属性更新现有的和新的模型。Rails - 如何管理嵌套属性而不使用accepts_nested_attributes_for?
我正在构建一个使用嵌套窗体的应用程序。我最初使用这个RailsCast作为蓝图(利用accept_nested_attributes_for):Railscast 196: Nested Model Form。
我的应用程序是清单与作业(任务),我让用户更新清单(名称,说明)和添加/删除关联的作业在一个单一的形式。这很好,但是当我将这个应用到我的应用程序的另一个方面时,我遇到了问题:通过版本控制的历史记录。
我的应用很大一部分是我需要记录我的模型和关联的历史信息。我结束了自己的版本(here是我的问题,我描述了我的决策过程/注意事项),其中很大一部分是我需要创建旧版本的新版本,对新版本进行更新的工作流程,存档旧版本。这对于用户来说是不可见的,他们将体验视为仅仅通过UI更新模型。
码 - 模型
#checklist.rb
class Checklist < ActiveRecord::Base
has_many :jobs, :through => :checklists_jobs
accepts_nested_attributes_for :jobs, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end
#job.rb
class Job < ActiveRecord::Base
has_many :checklists, :through => :checklists_jobs
end
码 - 目前的形式(注:@jobs被定义为这个清单中的清单控制器编辑动作未归档工作;所以是@checklist)
<%= simple_form_for @checklist, :html => { :class => 'form-inline' } do |f| %>
<fieldset>
<legend><%= controller.action_name.capitalize %> Checklist</legend><br>
<%= f.input :name, :input_html => { :rows => 1 }, :placeholder => 'Name the Checklist...', :class => 'autoresizer' %>
<%= f.input :description, :input_html => { :rows => 3 }, :placeholder => 'Optional description...', :class => 'autoresizer' %>
<legend>Jobs on this Checklist - [Name] [Description]</legend>
<%= f.fields_for :jobs, @jobs, :html => { :class => 'form-inline' } do |j| %>
<%= render "job_fields_disabled", :j => j %>
<% end %>
</br>
<p><%= link_to_add_fields "+", f, :jobs %></p>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to 'Cancel', checklists_path, :class => 'btn' %>
</div>
</fieldset>
<% end %>
代码 - checklists_controller.rb#代码片段#更新
def update
@oldChecklist = Checklist.find(params[:id])
# Do some checks to determine if we need to do the new copy/archive stuff
@newChecklist = @oldChecklist.dup
@newChecklist.parent_id = (@oldChecklist.parent_id == 0) ? @oldChecklist.id : @oldChecklist.parent_id
@newChecklist.predecessor_id = @oldChecklist.id
@newChecklist.version = (@oldChecklist.version + 1)
@newChecklist.save
# Now I've got a new checklist that looks like the old one (with some updated versioning info).
# For the jobs associated with the old checklist, do some similar archiving and creating new versions IN THE JOIN TABLE
@oldChecklist.checklists_jobs.archived_state(:false).each do |u|
x = u.dup
x.checklist_id = @newChecklist.id
x.save
u.archive
u.save
end
# Now the new checklist's join table entries look like the old checklist's entries did
# BEFORE the form was submitted; but I want to update the NEW Checklist so it reflects
# the updates made in the form that was submitted.
# Part of the params[:checklist] has is "jobs_attributes", which is handled by
# accepts_nested_attributes_for. The problem is I can't really manipulate that hash very
# well, and I can't do a direct update with those attributes on my NEW model (as I'm
# trying in the next line) due to a built-in limitation.
@newChecklist.update_attributes(params[:checklist])
这就是我运行i通过accept_nested_attributes_for的限制(它被记录得很好here。我得到了“ID = Y的Model2 ID = X的Model1无法找到”异常,这基本上与设计一致。
那么,我该如何创建多个嵌套模型,并在父模型的表单上添加/删除它们,类似于accept_nested_attributes_for的做法,但是我自己呢?
我见过的选项 - 是最好的选择之一吗? 真正的诀窍是我需要能够使用现有的关联/属性来更新现有模型和新模型。我无法链接它们,所以我只是将它们命名。
Redtape(在github) 了Virtus(也github上)
感谢您的帮助!
如果你解决了这个问题,我会非常感兴趣的看到你的解决方案。 – 2013-07-02 02:39:28
马里奥,我确实解决了它,并且我在下面发布了我的代码。这不是很好的代码,但是如果你在类似的事情上挣扎,也许会给你一些想法。任何问题,只是在这里或我的答案评论,我会尽力澄清,如果我可以。 – JoshDoody 2013-07-04 06:43:11