1

我有负责创建Foobar的“快速添加”表单。一旦提交/保存,您将返回到表单以添加下一个 Foobar。我现在有要在创建 Foobar 时添加到的多态任务。在这种情况下,任务不需要任何详细信息,因此我选择在表单上设置一个复选框,上面写着“执行此任务”。如果在提交表单时选中,我希望创建一个任务并将其关联到 Foobar。

这样做并不难,但我正在努力寻找似乎是“正确”的做法。

选项 1: Foobar 接受_nested_attributes_for 任务,因此我可以创建嵌套表单,但实际上没有什么可嵌套的。该复选框不代表任务上的有效字段,只是我希望创建任务。我可以使用@foobar.build_task 并在表单中为任务放置一些隐藏字段,但如果未选中复选框,我必须使用 JS voodoo 来防止字段提交。这似乎是肮脏和错误的。

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

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

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

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

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

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

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

4

2 回答 2

2

我会选择 Option 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文档accepts_attributes_for实施

于 2013-05-02T03:21:58.470 回答
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
于 2013-05-02T02:37:56.677 回答