user1153102
user1153102

Reputation: 65

rails 5 accepts_nested_attributes Not Working

I am just experiment with Rails 5, and came upon the accepts_nested_attributes_for. But I cannot get it to work. What am I doing wrong?

Here are my Models:

  1. Person
  2. Activity

Here are the SQL Table Definitions

Model Code

class Person < ApplicationRecord
  has_many :activities
  accepts_nested_attributes_for :activities
end


class Activity < ApplicationRecord
  belongs_to :person
end

Reading the API Docs (accepts_nested_attributes_for)

I created a params hash, and try to submit it to the Person Class.

params = {person: {name: 'Joe', activities_attributes: [{description: "Hiking"}]} }

When I try to Run this particular code, instead of creating a new person record named "Joe", and new association record under activities for "Hiking", it simple fails.

>> params = {person: {name: 'Joe', activities_attributes: [{description: "Hiking"}]} }
{:person=>{:name=>"Joe", :activities_attributes=>[{:description=>"Hiking"}]}}


>> Person.create(params[:person])
   (12.6ms)  BEGIN
   (0.6ms)  ROLLBACK
#<Person id: nil, name: "Joe", age: nil, adult: nil, created_at: nil, updated_at: nil>

But doing it one step at a time seems to work fine:

>> Person.create(:name => "Joe")
   (6.3ms)  BEGIN
  SQL (31.6ms)  INSERT INTO "people" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "Joe"], ["created_at", 2016-07-13 19:30:35 UTC], ["updated_at", 2016-07-13 19:30:35 UTC]]



>> a = Person.first
  Person Load (9.1ms)  SELECT  "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT $1  [["LIMIT", 1]]
#<Person id: 35, name: "Joe", age: nil, adult: nil, created_at: "2016-07-13 19:30:35", updated_at: "2016-07-13 19:30:35">


>> a.activities_attributes = [{:name => "Hiking"}]
[{:name=>"Hiking"}]


>> a.save
   (0.2ms)  BEGIN
  Person Load (0.6ms)  SELECT  "people".* FROM "people" WHERE "people"."id" = $1 LIMIT $2  [["id", 35], ["LIMIT", 1]]
  SQL (13.0ms)  INSERT INTO "activities" ("person_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["person_id", 35], ["name", "Hiking"], ["created_at", 2016-07-13 19:31:02 UTC], ["updated_at", 2016-07-13 19:31:02 UTC]]
   (6.8ms)  COMMIT
true


>> a
#<Person id: 35, name: "Joe", age: nil, adult: nil, created_at: "2016-07-13 19:30:35", updated_at: "2016-07-13 19:30:35">


>> a.activities
  Activity Load (12.4ms)  SELECT "activities".* FROM "activities" WHERE "activities"."person_id" = $1  [["person_id", 35]]
#<ActiveRecord::Associations::CollectionProxy [#<Activity id: 7, person_id: 35, name: "Hiking", created_at: "2016-07-13 19:31:02", updated_at: "2016-07-13 19:31:02", description: nil>]>

People Table

testing_development=# \d+ people;
                                                         Table "public.people"
   Column   |            Type             |                      Modifiers                      | Storage  | Stats target | Description 
------------+-----------------------------+-----------------------------------------------------+----------+--------------+-------------
 id         | integer                     | not null default nextval('people_id_seq'::regclass) | plain    |              | 
 name       | character varying           |                                                     | extended |              | 
 age        | integer                     |                                                     | plain    |              | 
 adult      | boolean                     |                                                     | plain    |              | 
 created_at | timestamp without time zone | not null                                            | plain    |              | 
 updated_at | timestamp without time zone | not null                                            | plain    |              | 
Indexes:
    "people_pkey" PRIMARY KEY, btree (id)

Activity Table

testing_development=# \d+ activities;
                                                          Table "public.activities"
   Column    |            Type             |                        Modifiers                        | Storage  | Stats target | Description 
-------------+-----------------------------+---------------------------------------------------------+----------+--------------+-------------
 id          | integer                     | not null default nextval('activities_id_seq'::regclass) | plain    |              | 
 person_id   | integer                     |                                                         | plain    |              | 
 name        | text                        |                                                         | extended |              | 
 created_at  | timestamp without time zone | not null                                                | plain    |              | 
 updated_at  | timestamp without time zone | not null                                                | plain    |              | 
 description | text                        |                                                         | extended |              | 
Indexes:
    "activities_pkey" PRIMARY KEY, btree (id)
    "index_activities_on_person_id" btree (person_id)

Upvotes: 4

Views: 3034

Answers (2)

ybakos
ybakos

Reputation: 8630

Using optional: true isn't really the right solution - it's a workaround. There is a bug in accepts_nested_attributes_for in Rails 5 versions prior to 5.1.1. See https://github.com/rails/rails/issues/25198 and Trouble with accepts_nested_attributes_for in Rails 5.0.0.beta3, -api option.

The solution is to use the inverse_of: option or, better yet, upgrade to Rails 5.1.1.

Upvotes: 0

user1153102
user1153102

Reputation: 65

I got it to work, but not sure if this is an error with Rails 5, or just a change in how things are done.

How I got it to work was to see if this problem was isolated to rails 5. I created a Rails 4.2 app, with the same exact code, and it worked.

Once I got it to work, then I looked at the posts online to see if I could find why this was occuring in rails. These are the resources that helped me:

Trouble with accepts_nested_attributes_for in Rails 5.0.0.beta3, -api option

class Post < ApplicationRecord
  belongs_to :member, optional: true
end

I added that line, and it worked like a charm.

>> params = {person: {name: "Testing", activities_attributes: [{name: "Testing"}]}}
{:person=>{:name=>"Testing", :activities_attributes=>[{:name=>"Testing"}]}}


>> Person.create(params[:person])
   (0.1ms)  BEGIN
  SQL (6.5ms)  INSERT INTO "people" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "Testing"], ["created_at", 2016-07-13 21:30:45 UTC], ["updated_at", 2016-07-13 21:30:45 UTC]]
  SQL (12.5ms)  INSERT INTO "activities" ("person_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["person_id", 37], ["name", "Testing"], ["created_at", 2016-07-13 21:30:45 UTC], ["updated_at", 2016-07-13 21:30:45 UTC]]
   (2.9ms)  COMMIT
#<Person id: 37, name: "Testing", age: nil, adult: nil, created_at: "2016-07-13 21:30:45", updated_at: "2016-07-13 21:30:45">

This page shows on what changed in Rails 5 to have this behavior.

http://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html

Upvotes: 2

Related Questions