使用accepts_nested_attributes_for创建或删除关联

时间:2010-11-02 14:32:33

标签: ruby-on-rails nested-attributes

我有2个模型,CategoryLineItemTypes

两者已经存在很多,现在需要将它们联系起来。每个类别都有许多LineItemType。

我在accepts_nested_attributes_for :line_item_types

上添加了Category

我尝试在表单上使用hidden_field来创建现有关联LineItemTypes的列表:

- form_for @category do |form|
  %ul#categorised
    - form.fields_for :line_item_types do |line_item_types|
      -categorised.each do |l|
        %li
          =l.description
          =line_item_types.hidden_field :category_id

  =form.submit

如果我在该列表中添加项目,则会收到错误消息,指出无法找到该类别的LineItemType。我认为accepts_nested_attributes_for会添加关联,如果它不存在。或者仅用于“创建”新记录和修改现有关系,而不是创建新关系。

a.update_attributes({:line_item_types_attributes => [{:id => 2767}, {:id => LineItemType.find(2).id}]})
ActiveRecord::RecordNotFound: Couldn't find LineItemType with ID=2 for Category with ID=1

任何想法,而不必写一些东西来遍历结果形式参数并创建关联?或者更简单的方法来实现这个目标?

3 个答案:

答案 0 :(得分:2)

我得出的结论是,accept_nested_attributes_for的工作方式有点像url_for ...... ID的存在使得它假设关系存在。渲染accepts_nested_attributes_for不适合我想做的事。

我使用之前的过滤器解决了这个问题:

def find_line_item_types
  params[:category][:line_item_types] = LineItemType.find(params[:category][:line_item_types].collect { |a| a[0].to_i }) if params[:category] and params[:category][:line_item_types]
end

答案 1 :(得分:0)

不能在没有在line_item_types属性中指定category_id的情况下从Category实例创建line_item_types!

你应该检查你的has_many和belongs_to声明中是否有:inverse_of选项。

# in Category
has_many :line_item_types, :inverse_of => :category

# In LineItemType
belongs_to :category, :inverse_of => :line_item_types

告诉我这是否有帮助。

答案 2 :(得分:0)

好吧,我遇到了同样的问题,所以我搜索了源码并修补了accepts_nested_attributes_for以允许这种行为...

https://gist.github.com/2223565

它看起来很多,但实际上我只修改了几行:

module ActiveRecord::NestedAttributes::ClassMethods
  def accepts_nested_attributes_for(*attr_names)
    # ...
    #options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
    options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only, :allow_existing)
    # ...
  end
end

和...

module ActiveRecord::NestedAttributes
  def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {})
    # ...
    #existing_records = if association.loaded?
    #  association.target
    #else
    #  attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
    #  attribute_ids.empty? ? [] : association.scoped.where(association.klass.primary_key => attribute_ids)
    #end

    existing_records = if options[:allow_existing] or not association.loaded?
      attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
      scope = options[:allow_existing] ? association.target_scope : association.scoped
      scope.where(association.klass.primary_key => attribute_ids)
    else
      association.target
    end
    # ...
  end
end