如何避免在嵌套导轨窗体上保存空记录

问题描述:

我正在使用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?使我的空白addrs的周围通过审定
  2. a[:id].blank?避免排斥反应,当用户从空白和现有的记录

现在,我需要删除(而不是保存)现有的Addr当用户空白value。此外,我通过RESTful API公开了人员和地址。我看到了两个可能的选择:

  1. 后处理的params哈希添加神奇_destroy=1 PARAM。 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 
+0

在这两者中,使用选项1.你不希望“神奇”喜欢“如果X字段的值为空,然后删除记录”。 – Zabba 2012-07-15 05:53:31

+0

我用您建议的解决方案更新了问题。 – 2012-07-15 19:39:33

+0

@Zabba,18个月后我重构了这段代码,你是对的。我将这个价值作为一个“神奇的”'destroy_blank_addrs'消失的想法是脑死亡。我也相信任何涉及直接修改'params'阵列的解决方案都是不好的做法。任何后期处理都应该在'assign_attributes'之后完成,但是在保存之前完成。 – 2015-02-14 21:51:55

第三种选择是在Perso上添加一个before_save回调n将删除所有空白的地址。这个想法有一些好处,但我可能不会去用它。

在您提供的两个选项中,我不会去处理参数。它会工作,但它太多的工作。此外,控制器代码会变得更加混乱,我坚信一个非常纤细的控制器。

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

如果我们正在讨论最干净的解决方案(在我看来),我会介绍第三个类来处理所有的逻辑并让控制器委托它。该控制器很容易进行单独测试,然后您可以编写一个模型规格来检查所有基本细节。这是一个更多的工作,我现在想不到一个好名字(PersonUpdater?),但它可能是一个值得思考的想法。

+1

感谢您的深思熟虑,Stefan。第三班是最干净的解决方案,但费力。将记录放入可用于某个异步进程的数据库(即使只是瞬间)的想法似乎是错误的。另一个想法是在JS的前端管理它。消隐只是按下删除按钮的另一种方式。然后消隐从来没有把它变成我的REST API。 – 2012-07-20 14:10:26

+0

在这种情况下,我将这个代码放在控制器中。测试有点麻烦,而且对于控制器应该是什么样的信念而言,看起来很混乱,但它应该是您的案例中最干净的解决方案。 – 2012-07-20 18:33:56

+0

谢谢,我使用了控制器方法,因为我觉得它更简单,它增加了我的API的功能,消费者可以通过消隐值来删除Addrs。如果你更新你的答案,我会接受。 – 2012-07-27 18:39:54

accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    :reject_if => proc { |att| att[:name].blank? && attr[:description].blank? } 

accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    reject_if: -> { |attr| [name, description].any? &:blank? } 
+0

非常简洁。我包含一个'valid?'测试,因此在用户完成解析验证之前,行不会被拒绝。 – 2015-02-14 22:25:59

accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    reject_if: :all_blank 
+0

允许您指定一个Proc或一个符号指向一个方法,该方法检查是否应为某个属性哈希构建记录。散列被传递给提供的Proc或方法,它应该返回true或false。如果指定no:reject_if,则将为所有没有_destroy值且值为true的属性散列构建一条记录。传递:all_blank而不是Proc将创建一个proc,它将拒绝一个记录,其中所有属性都为空,但不包含_destroy的任何值。 – hadees 2016-12-22 01:29:15