ruby-on-railsruby-on-rails-3

Saving array of nested attributes with accepts_nested_attributes_for


Project and tasks have a one-to-many relationship, and project accepts_nested_attributes_for :tasks.

In the form, my task objects look like:

project[tasks][2][assigned_time]
project[tasks][2][due_time]

When the form is submitted I get a hash like:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"...=", "project"=>{"id"=>"1", "tasks"=>{"1"=>{"assigned_time"=>"09:00", "due_time"=>"17:00"}, "2"=>{"assigned_time"=>"09:00", "due_time"=>"17:00"}}

Then I expect them to be saved by just saving the project object:

project = Project.find(params[:id])

respond_to do |format|
  if project.update_attributes(params[:tasks])

But I get:

WARNING: Can't mass-assign protected attributes: id SQL (0.3ms) ROLLBACK Completed in 169ms

ActiveRecord::AssociationTypeMismatch (Task(#2188181260) expected, got Array(#2151973780)):

Any ideas how to fix this?


Solution

  • In your Projects model, accepts_nested_attributes_for :tasks. This will define @project.tasks_attributes= if you have a has_many :tasks association or @project.task_attributes= if you have a has_one :task association.

    In your form, the following:

    = form_for @project do |f|
      = f.label :project_attribute
      = f.text_field :project_attribute
    
      = f.fields_for :tasks do |t|
        = t.label :task_attribute
        = t.text_field :task_attribute
    

    In your projects controller, the following:

    def new
      @project = Project.new
      @project.tasks.build #=> if has_many
      @project.build_task  #=> if has_one
    end