assign_nested_attributes_for_collection_association(association_name, attributes_collection)
private
Assigns the given attributes to the collection association.
Hashes with an :id value matching an existing associated record
will update that record. Hashes without an :id value will build a
new record for the association. Hashes with a matching :id value
and a :_destroy key
set to a truthy value will mark the matched record for destruction.
For example:
assign_nested_attributes_for_collection_association(:people, {
'1' => { id: '1', name: 'Peter' },
'2' => { name: 'John' },
'3' => { id: '2', _destroy: true }
})
Will update the name of the Person with ID 1,
build a new associated person with the name ‘John’, and mark the
associated Person with ID 2 for destruction.
Also accepts an Array of attribute hashes:
assign_nested_attributes_for_collection_association(:people, [
{ id: '1', name: 'Peter' },
{ name: 'John' },
{ id: '2', _destroy: true }
])
Show source
def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
options = nested_attributes_options[association_name]
if attributes_collection.respond_to?(:permitted?)
attributes_collection = attributes_collection.to_h
end
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
raise ArgumentError, "Hash or Array expected for attribute `#{association_name}`, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
end
check_record_limit!(options[:limit], attributes_collection)
if attributes_collection.is_a? Hash
keys = attributes_collection.keys
attributes_collection = if keys.include?("id") || keys.include?(:id)
[attributes_collection]
else
attributes_collection.values
end
end
association = association(association_name)
existing_records = if association.loaded?
association.target
else
attribute_ids = attributes_collection.map { |a| a["id"] || a[:id] }.compact
attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
end
attributes_collection.each do |attributes|
if attributes.respond_to?(:permitted?)
attributes = attributes.to_h
end
attributes = attributes.with_indifferent_access
if attributes["id"].blank?
unless reject_new_record?(association_name, attributes)
association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
end
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes["id"].to_s }
unless call_reject_if(association_name, attributes)
target_record = association.target.detect { |record| record.id.to_s == attributes["id"].to_s }
if target_record
existing_record = target_record
else
association.add_to_target(existing_record, skip_callbacks: true)
end
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
end
else
raise_nested_attributes_record_not_found!(association_name, attributes["id"])
end
end
end