【问题标题】:how to avoid saving empty records on a nested rails form如何避免在嵌套的 rails 表单上保存空记录
【发布时间】:2012-07-14 23:59:18
【问题描述】:

我将nested_form gem 用于我的AddressBook 关系。当用户清除现有Addr 的值时,我想删除该Addr 而不是用空白value 保存

class Person < ActiveRecord::Base
  has_many :addrs, dependent: :destroy
  attr_accessible :name, :addrs_attributes
  accepts_nested_attributes_for :addrs, reject_if: :addr_blank, allow_destroy: true

  def addr_blank(a)
    valid? && a[:id].blank? && a[:value].blank? 
  end

class Addr < ActiveRecord::Base
  belongs_to :person
  attr_accessible :kind, :label, :value, :person_id

我的:reject_if 方法效果很好,但它并没有给我所需的一切

  1. valid? 通过验证保留我的空白地址
  2. a[:id].blank? 在用户空白和现有记录时避免拒绝

现在,当用户将value 空白时,我需要删除(而不是保存)现有的Addr。另外,我通过 RESTful API 公开 Persons 和 Addrs。我看到了两种可能的选择:

  1. 后处理params 哈希以添加神奇的_destroy=1 参数。 IOW,模拟按下删除按钮的用户活动。
  2. 将其封装在 Addr 模型中,以便有效地将带有空白 value 的更新视为删除。

根据这里的建议,我是如何实现它的:

people_controller.rb

def update
  @person = Person.find(params[:id])
  @person.destroy_blank_addrs(params[:person])
  respond_to do |format|
  ...

person.rb

def destroy_blank_addrs(person_params)
  if valid? && person_params[:addrs_attributes]
    person_params[:addrs_attributes].each do |addr_params_array|
      addr_params= addr_params_array[1] 
      addr_params[:_destroy] = '1' if !addr_params[:id].blank? && addr_params[:value].blank? 
    end
  end
end

【问题讨论】:

  • 两者中,使用选项1。你不希望像“如果X字段的值为空则删除记录”这样的“魔术”。
  • 我用您建议的解决方案更新了问题。
  • @Zabba,我在 18 个月后重构了这段代码,你是对的。我将值空白为“神奇”destroy_blank_addrs 的想法是脑残。我也相信任何涉及直接修改 params 数组的解决方案都是不好的做法。任何后处理都应在assign_attributes 之后但在save 之前完成

标签: ruby-on-rails nested-forms


【解决方案1】:
accepts_nested_attributes_for :addrs, 
  allow_destroy: true, 
  :reject_if => proc { |att| att[:name].blank? && attr[:description].blank? }

【讨论】:

    【解决方案2】:
    accepts_nested_attributes_for :addrs, 
      allow_destroy: true, 
      reject_if: -> { |attr| [name, description].any? &:blank? }
    

    【讨论】:

    • 非常简洁。我包含一个valid? 测试,因此在用户完成验证验证之前不会拒绝行。
    【解决方案3】:

    第三种选择是在 Person 上添加一个 before_save 回调,这将删除所有空白地址。这个想法有一些优点,但我可能不会接受。

    在您提供的两个选项中,我不会对参数进行后处理。会成功的,但工作量太大。此外,控制器代码会变得有点混乱,我坚信控制器是非常纤薄的。

    在我看来,最简单的选择是在保存后删除空白地址。您可以添加Person#remove_blank_addresses(),然后在成功保存时调用它。您不需要传入参数 - 它可以迭代地址并删除空白地址。它的缺点是创建空地址然后销毁它们,但无论如何更新人员都需要它。

    如果我们谈论的是最简洁的解决方案(在我看来),我会引入第三个类来处理所有这些逻辑并让控制器委托给它。控制器很容易单独测试,然后您可以编写一个模型规范来检查所有细节。这有点工作,我现在想不出一个好名字(PersonUpdater?),但这可能是一个值得考虑的想法。

    【讨论】:

    • 感谢 Stefan 的周到回复。三等舱是最干净的解决方案,但需要付出太多努力。将记录放入某些异步进程(即使只是瞬间)可用的数据库的想法似乎是错误的。另一个想法是在 JS 中在前端进行管理。消隐只是按下删除按钮的另一种方式。然后,Blanking 永远不会进入我的 REST api。
    • 在这种情况下,我会在控制器中有这段代码。测试起来有点棘手,并且在我对控制器中应该包含什么的信念中看起来有点混乱,但它应该是你情况下最干净的解决方案。
    • 谢谢,我选择了控制器方法,因为我觉得它更简单,并且它为我的 API 添加了功能,消费者可以通过清空值来删除 Addr。如果你更新你的答案,我会接受。
    • 现在我正在重构此代码,将其移至 ServiceObject(如您所建议的那样)是最好的解决方案。我将它命名为PersonTracker,因为它还负责更新activity 日志。有关详细信息,请参阅railscasts.com/episodes/398-service-objects
    【解决方案4】:
    accepts_nested_attributes_for :addrs, 
      allow_destroy: true, 
      reject_if: :all_blank
    

    【讨论】:

    • 允许您指定一个 Proc 或一个 Symbol 指向一个检查是否应该为某个属性哈希构建记录的方法。哈希被传递给提供的 Proc 或方法,它应该返回 true 或 false。当没有指定 :reject_if 时,将为所有不具有评估为 true 的 _destroy 值的属性哈希构建记录。传递 :all_blank 而不是 Proc 将创建一个 proc,它将拒绝所有属性为空的记录,不包括 _destroy 的任何值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多