10
votes

I am using Rails 5.0.0.beta3, building an API-only app using the -app option on rails new, and I am having trouble with accepts_nested_attributes_for.

In my app, a create (or a new, then a save!) of an object with nested attributes fails, with a message that the parent parent object must exist.

To test, I made a new app and used just the test case with members and posts in the ANAF documentation:

class Member < ApplicationRecord  
  has_many :posts  
  accepts_nested_attributes_for :posts  
end  

and

class Post < ApplicationRecord
 belongs_to :member
end

(These class definitions were generated by the Rails scaffold generator, so the inherit from ApplicationRecord, rather than ActiveRecord::Base, but per this post, that is not significant.)

With those classed defined, and matching migrations created and run, I launch a Rails console and follow the steps in the doc:

params = { member: {
name: 'joe', posts_attributes: [
    { title: 'Kari, the awesome Ruby documentation browser!' },
    { title: 'The egalitarian assumption of the modern citizen' },
    { title: '', _destroy: '1' } # this will be ignored
]}}  

{:member=>{:name=>"joe", :posts_attributes=>[{:title=>"Kari, the awesome Ruby documentation browser!"}, {:title=>"The egalitarian assumption of the modern citizen"}, {:title=>"", :_destroy=>"1"}]}}

And then:

>> member = Member.create(params[:member])
 (0.2ms)  BEGIN
 (0.4ms)  ROLLBACK
#<Member id: nil, name: "joe", created_at: nil, updated_at: nil>

No joy!

When I split the create into new, then save!, I get the same result, with a somewhat clearer error:

>> member = Member.new(params[:member])  
#<Member id: nil, name: "joe", created_at: nil, updated_at: nil>

member.save!
(15.0ms) BEGIN ActiveRecord::RecordInvalid: Validation failed: Posts member must exist
from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/validations.rb:78:in raise_validation_error'
from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/validations.rb:50:in
save!' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in save!' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/transactions.rb:324:inblock in save!' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/transactions.rb:395:in block in with_transaction_returning_status' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:233:inblock in transaction' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in within_new_transaction' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:233:intransaction' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/transactions.rb:211:in transaction' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/transactions.rb:392:inwith_transaction_returning_status' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/transactions.rb:324:in save!' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/activerecord/lib/active_record/suppressor.rb:45:insave!' from (irb):14 from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/railties/lib/rails/commands/console.rb:65:in start' from /Users/pauldavis/.rvm/gems/ruby-2.2.4 /bundler/gems/rails-b785064958f9/railties/lib/rails/commands/console_helper.rb:9:instart' (0.2ms) ROLLBACK from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/railties/lib/rails/commands/commands_tasks.rb:78:in console' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/railties/lib/rails/commands/commands_tasks.rb:49:inrun_command!' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/railties/lib/rails/command.rb:20:in run' from /Users/pauldavis/.rvm/gems/ruby-2.2.4/bundler/gems/rails-b785064958f9/railties/lib/rails/commands.rb:18:in' from /Users/pauldavis/Documents/Projects/Active/Rails/curious/doko/m.0/test_anaf/bin/rails:9:in require' from /Users/pauldavis/Documents/Projects/Active/Rails/curious/doko/m.0/test_anaf/bin/rails:9:in' from -e:1:in load' from -e:1:in'

Any thoughts on why this sample code in the documentation is be working? Could something be wrong in my environment? Does the -api option break something in ActiveRecord? BTW, I am using PostgreSQL

Thanks!

5
I have been searching for a solution for some hours, but I found a solution today, in this helpful blog post. I think the optional: parameter should be mentioned in the docs, because it is required to make the code work as shown. Perhaps the docs should also be updated changing the superclass to ApplicationRecord, too, though that seems less compelling.PJD Barna

5 Answers

9
votes

I also encountered the same problem.
It seems specification has been changed from Rails 5.
By putting the option, and the previous and the same operation.

belongs_to should default to required: true #18233

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

It is my clumsy may speak English, but ...

5
votes

This is a regression reported as rails#25198. As was pointed out, you may use inverse_of as a workaround.

It is planned to be fixed in 5.0.1.

3
votes

I had the same problem and I found the solution in https://github.com/rails/rails/issues/18233

Just add

class Post < ApplicationRecord
belongs_to :member, required: false
end

It seems a problem with accepts_nested_attributes_for in Rails 5, so you need add required: false to your child models.

1
votes

I found that adding the inverse_of options to the associations allowed accepts_nested_attributes to work. Nice than monkey patching, still allows the association to be validated

0
votes

I am experiencing problems with accepts_nested_attributes_for as well in my Rails 5 beta 3 app and seems like it is buggy. Ideally, a bug report should be submitted, but we didn't have time to do it properly. We have the following setup:

accepts_nested_attributes_for :attachments, allow_destroy: true

Eventually, we had to monkey-patch the method inside the model like this:

  def attachments_attributes=(attributes)
    attributes.reject! do |_attachment|  
      if _attachment = Attachment.find(_attachment['id'])
        if _attachment.drop_id.nil?
          attachments << _attachment
          next true
        end
      end
      next false
    end
    # assign_nested_attributes_for_collection_association(:attachments, attributes)
  end

The only thing is that the last (commented-out line) with assign_nested_attributes_for_collection_association has some issues, but hopefully this will provide you an idea how this can be fixed.