【问题标题】:How to create nested objects using accepts_nested_attributes_for如何使用 Accept_nested_attributes_for 创建嵌套对象
【发布时间】:2009-07-30 20:36:30
【问题描述】:

我已经升级到 Rails 2.3.3(从 2.1.x),我正在尝试找出 accepts_nested_attributes_for 方法。我可以使用该方法更新现有的嵌套对象,但不能使用它来创建新的嵌套对象。给定一个人为的例子:

class Product < ActiveRecord::Base
  has_many :notes
  accepts_nested_attributes_for :notes
end

class Note < ActiveRecord::Base
  belongs_to :product
  validates_presence_of :product_id, :body
end

如果我尝试创建一个新的Product,并带有一个嵌套的Note,如下:

params = {:name => 'Test', :notes_attributes => {'0' => {'body' => 'Body'}}}
p = Product.new(params)
p.save!

消息验证失败:

ActiveRecord::RecordInvalid: Validation failed: Notes product can't be blank

我明白为什么会发生这种情况 - 这是因为 Note 类上的 validates_presence_of :product_id,并且因为在保存新记录时,Product 对象没有 id。但是,我不想删除此验证;我认为删除它是不正确的。

我也可以通过先手动创建Product,然后添加Note 来解决问题,但这会破坏accepts_nested_attributes_for 的简单性。

是否有在新记录上创建嵌套对象的标准 Rails 方法?

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    这是一个常见的循环依赖问题。有一个existing LightHouse ticket 值得一看。

    我希望这在 Rails 3 中会得到很大改进,但与此同时,您必须采取一种变通方法。一种解决方案是设置一个您在嵌套时设置的虚拟属性,以使验证有条件。

    class Note < ActiveRecord::Base
      belongs_to :product
      validates_presence_of :product_id, :unless => :nested
      attr_accessor :nested
    end
    

    然后您可以将此属性设置为表单中的隐藏字段。

    <%= note_form.hidden_field :nested %>
    

    在通过嵌套表单创建注释时设置nested 属性就足够了。 未经测试。

    【讨论】:

    • 在 Rails 3 中,通过向 has_many、has_one 和 belongs_to 添加 :inverse_of 选项解决了这个问题。参见例如daokaous.com/rails3.0.0_doc/classes/ActiveRecord/Associations/… 在“双向关联”部分下
    • 我选择在 :id == nil 时禁用验证。由于这只会在编写新的嵌套记录时发生,我希望这会是安全的。奇怪的是这个问题一直到 2.3.8。
    • inverse_of 在记录的父项都不存在时似乎不起作用,因此无法测试 id。看起来这个嵌套的hack是唯一的方法。 :(
    • 我在 Rails 3 中遇到了这个问题,有什么更新可以解决它吗?
    【解决方案2】:

    【讨论】:

    • 非常重要的一点是您应该验证“product”而不是“product_id”的存在
    • 我认为 kuboon 有正确的答案。验证“product”的存在,而不是“product_id”,并将 Product 上的关联更改为“has_many :notes, inverse_of: :product”。
    【解决方案3】:

    Ryan 的解决方案实际上非常酷。 我去让我的控制器更胖,这样这个嵌套就不必出现在视图中。主要是因为我的视图有时是 json,所以我希望能够在其中尽可能少地逃脱。

    class Product < ActiveRecord::Base
      has_many :notes
      accepts_nested_attributes_for :note
    end
    
    class Note < ActiveRecord::Base
      belongs_to :product
      validates_presence_of :product_id  unless :nested
      attr_accessor :nested
    end
    
    class ProductController < ApplicationController
    
    def create
        if params[:product][:note_attributes]
           params[:product][:note_attributes].each { |attribute| 
              attribute.merge!({:nested => true})
        }
        end
        # all the regular create stuff here  
    end
    end
    

    【讨论】:

      【解决方案4】:

      目前最好的解决方案是使用 parental_control 插件:http://github.com/h-lame/parental_control

      【讨论】:

        猜你喜欢
        • 2011-07-17
        • 1970-01-01
        • 2021-06-09
        • 2020-04-02
        • 2016-02-04
        • 2019-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多