Creating or deleting associations with accepts_nested_attributes_for
I have 2 models, Category
and LineItemTypes
There are already plenty existing o开发者_如何学Pythonf both, it is now a requirement for them to be associated. Being many LineItemTypes for every Category.
I have added accepts_nested_attributes_for :line_item_types
on Category
I've tried using a hidden_field
on a form to create a list of existing associated 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
If I add an item to that list, I get errors saying that a LineItemType for that Category can't be found. I thought accepts_nested_attributes_for would add the association if it doesn't exist. Or is it only for 'creating' new records and modifying existing relationships, not creating new relationships.
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
Any ideas without having to write something to traverse the resulting form params and create the associations? Or an even easier way to achieving this?
I've come to the conclusion that accepts_nested_attributes_for works kinda like url_for... Where the presence of ID makes it assume the relationship exists. Rendering accepts_nested_attributes_for not suitable for what I want to do.
I've worked around this with a before filter:
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
no you should be able to create line_item_types from a Category instance without specifying the category_id in the line_item_types attributes!
You should check that you got the :inverse_of option in your has_many and belongs_to declarations.
# in Category
has_many :line_item_types, :inverse_of => :category
# In LineItemType
belongs_to :category, :inverse_of => :line_item_types
Tell me if that helps.
Well, I had the same issue, so I searched into the source and monkey patched accepts_nested_attributes_for
to allow this behavior...
https://gist.github.com/2223565
It looks like a lot, but I actually just modified a few lines:
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
And...
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
精彩评论