谁负责在Rails中创建关联的has_many模型?

时间:2013-05-01 23:38:42

标签: ruby-on-rails model-view-controller polymorphic-associations

我有“快速添加”表单,负责创建 Foobar 。提交/保存后,您将返回到表单以添加下一个Foobar。我现在有多态任务,我想要在Foobar创建的时候添加它。任务在此上下文中不需要任何详细信息,因此我选择在表单上显示“执行此任务”的复选框。如果在提交表单时检查,我想创建一个任务并与Foobar相关联。

这样做并不难,但我很难找到似乎是“正确”的做法。

选项1: Foobar accepted_nested_attributes_for任务,所以我可以创建一个嵌套表单,但实际上没有任何东西可以嵌套。该复选框不代表任务上的有效字段,只是我想要创建的任务。我可以使用@ foobar.build_task并在表单中放置一些隐藏字段用于任务,但如果未选中复选框,我必须使用JS voodoo来阻止字段提交。这看起来很脏又错。

选项2:我可以在FoobarController #veve中添加一些逻辑,查找复选框并在保存之前将任务构建到Foobar上。这里的问题是Task是多态的,也可能与其他东西联系在一起。甚至可能有多个“类型”的任务与正在创建的单个Foobar相关联。我认为这个解决方案比选项1好,但不是很干。控制器中的任何此类逻辑最终都会在控制器中被复制以用于其他可执行的任务。

选项3:在Foobar上有一个before_save,它寻找存在名为“create_task”的伪字段,然后构建任务。这将复制从控制器转移到可任务模型中,但它不再是每个模型中重复的“has_many:tasks”行的重复。尽管如此,寻找这样一个领域并采取行动似乎并不是模特的工作。

所以....我真的很感激一些想法。

更新#1:一些额外信息......

任务还有一个创建者和一个受让人,两者都是系统中的用户。应该基于current_user方法自动分配创建者,当然可以在View和Controller中使用,但不能在模型中使用。我认为立即排除答案纯粹基于模型的可能性(例如选项3)。我认为它也暗示答案不是基于视图(例如选项1),因为用户应该设置服务器端,它不能被篡改。所以也许答案就在控制器的某个地方?也许是某种辅助方法来包装要在每个处理可执行任务的Controller中复制的逻辑?

更新#2:我目前的倾向......

我花了一些时间与一位受人尊敬的开发者朋友交谈,并进一步确信答案最接近选项2.视图和模型都显得不对。控制器很有意义,但主要问题是重复代码的可能性。我认为答案是找到最佳方法来分解控制器代码,该代码处理任务(或其他多态事物,如注释,文件上传等)的附件到控制器负责的对象。当我有一个我很满意的解决方案时,我会尝试在这里发布。感谢大家的投入!

2 个答案:

答案 0 :(得分:2)

我会选择选项1 的衍生产品。实际上我可能会使用两种解决方案。

解决方案1 ​​

这个更简单但不太可扩展。

使用现有的accepts_nested_attributes_for :tasks,请确保您拥有此选项:

accepts_nested_attributes_for :tasks, :allow_destory => true

然后在你的表单中使用(假设task_collection是预构建任务的集合):

<%= form_for(@record) do |form| %>
   ...
   <%= form.fields_for :tasks, task_collection do |task_fields| %>
     <%= task_fields.check_box(:_destroy,{},0,1) %> Do This Task
   <% end %>
   ...
<% end %>

基本上这将采用Task关联对象,如果选中该框,则将其保留,否则将其标记为销毁。这应该可以防止任何所谓的“ JS voodoo ”,因为在模型中处理任何此类逻辑。

解决方案2

其他选项,但更复杂的选项,我可能会使用:

def tasks_attributes=(tasks_attributes)

在每个适用的模型(或包含的模块)中执行该操作,然后在从表单提交时处理所有任务属性。

它涉及处理模型中的task_attributes。这意味着你必须以某种方式和其他一些细微的细节构建你的表单。但是使用这种方法,您不必构建Task对象然后销毁它,您只需处理提交数据即可创建tasks

这样做的细节非常深入,而不是我真正想要的内容。但你似乎足够聪明,只要知道它可以帮助你。

有关此内容的更多信息,请访问fields_for documentationaccepts_attributes_for implementation

答案 1 :(得分:0)

在这种情况下,我会这样:

class Foobar
  has_many :tasks
  attr_accessor :add_task
  after_create :create_task, if: add_task == true

  private
  def create_task
    tasks.create(...)
  end
end