【问题标题】:Why is directly manipulating the Rails form params hash considered code smell?为什么直接操作 Rails 表单参数哈希被认为是代码异味?
【发布时间】:2012-07-13 10:21:22
【问题描述】:

我有一个 Rails 表单,其中包含一个父模型和另一个模型的可能多个子节点的嵌套属性。

子模型有一个属性,该属性在逻辑中作为数组进行操作,但使用 Rails 内置的序列化方法序列化为 YAML 字符串。

在表单中,我显示了数组的每个单独成员,以便用户可以选择性地删除成员。

当用户销毁所有成员时会出现问题。表单不会将参数的任何值传递给 Rails 控制器,当调用 UPDATE 操作时,它会忽略该属性,因为在表单参数哈希中没有它的键。这当然是复选框之类的已知问题,因此 Rails 会自动为每个复选框放置 2 个复选框 HTML 元素,一个隐藏的只有在复选框被选中时才会处理。

我在这里处理的不是复选框,而是隐藏的输入文本字段。

我实现的解决方案是直接在控制器的 UPDATE 动作中操作 params 哈希,像这样:

params[:series][:time_slots_attributes].each { |k,v| v[:exdates] ||= [] }

这算不算代码异味?

我是否应该添加一个额外的隐藏字段,该字段已禁用并且仅在用户删除最后一个成员时才启用?这个解决方案也很有效,但对我来说似乎很笨重。

【问题讨论】:

  • @Deefour,这并不能解决问题。问题是当 UPDATE 操作处理 params 哈希时,需要将一个空数组写入 DB。我想我可以检查 params 值以查看密钥是否存在,如果不存在,我可以酌情为每个孩子单独更新该属性。
  • 类似这样的东西:params[:series][:time_slots_attributes].each_with_index do |attribs,idx| @series.time_slots[idx].update_attribute(:exdates, []) unless attribs[1].has_key?(:exdates) end
  • 你问是不是代码异味;这是我唯一回应的一件事。
  • @Deefour,还好吧……谢谢。我正在寻找的是对为什么操纵 pa​​rams 哈希是一种不好的做法的更深入的解释。如果我对所有影响有更广泛的了解,我可以更好地决定是否要在这个例外中违反最佳实践。
  • 这是应该在模型中完成的应用程序逻辑(即将参数哈希传递给模型中的方法)。

标签: html ruby-on-rails ruby-on-rails-3 forms


【解决方案1】:

这远不是一个详尽的答案...但是在考虑了这个问题之后,我可以看到一个问题是,如果将来构建利用相同 UPDATE 操作的表单,则会发生意外行为,这违反了最少原则惊喜。如果稍后构建第二个表单,它不希望更改 exdates 属性的值(因为它没有传递它们),则 UPDATE 操作无论如何都会将一个空数组写入该属性。

我决定通过添加一个具有真正布尔值的隐藏表单字段来解决此问题,然后在将所有时隙 exdates 设置为空数组之前检查该值。这样,如果未来的开发人员创建了一个利用系列控制器的 UPDATE 操作的新表单,他们将不会遇到将其 exdates 设置为空数组的意外行为。如果他们想在他们的表单中处理 exdates,他们需要具有相同的隐藏表单字段和真值。这似乎是一个更简单的解决方案,然后为 exdates、migration 和 AR 关联添加一个类和表,并添加另一层嵌套属性,这样我不仅有父属性和子属性,还包含父、子和孙子。这个解决方案有点像 Rails hack,用于处理带有第二个隐藏复选框字段的复选框。

【讨论】:

    【解决方案2】:

    这在 NestedAttributes 模块中通过允许“_destroy”参数触发对该特定嵌套属性的销毁调用来处理:

    http://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for.

    如果您没有使用嵌套属性(您可能应该使用嵌套属性,在很多情况下它都非常简洁),那么是的,您必须自己手动处理一些东西,通过确定哪些值应该 em> 一直在场并与他们一起做一些特别的事情。

    【讨论】:

    • 我们使用accepts_nested_attributes_for 和:allow_destroy => true,但这仅适用于复选框表单字段吗?
    • 可能我这里有点糊涂了,但是我觉得“_destroy”参数是要销毁子对象,而不是嵌套属性?我不是要破坏整个子对象。我试图允许用户保留一个数组属性,该属性要么(a)未更改,(b)已更改但仍有值,要么(c)不再具有值。 (a) 和 (b) 都不是问题。 (c) 当给定属性的所有输入标签都被禁用时不起作用,该属性根本不会出现在 params 哈希中,因此不会对属性进行任何更改。
    • 我应该补充一点,我的示例使用父子模型这一事实是无关紧要的。同样的问题出现在一个简单的单一模型表单中。我的示例是从一个实际的当前问题中选择的,该问题顺便是在一个使用嵌套属性的表单中,但又与问题无关。
    • 好的,所以基本上我不应该将此数据序列化为字符串,因为它阻止我将每个数组元素视为子对象,因此我可以在使用 accept_nested_attributes_for 处理表单时单独销毁每个元素, :allow_destroy => true ?
    猜你喜欢
    • 2016-10-17
    • 2015-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多