Search code examples
ruby-on-railsnested-attributesrails-consolecocoon-gem

Rollback when creating object with nested attributes


I have these models and I am using cocoon with nested_attributes:

models/report.rb:

class Report < ApplicationRecord
  has_many :option_students

  accepts_nested_attributes_for :option_students, allow_destroy: true
end

models/option_students.rb:

class OptionStudent < ApplicationRecord
  belongs_to :student
  belongs_to :option
  belongs_to :report
end

I am trying to create a report using rails console. I already have a student and a option saved on DB.

If I write:

Report.create(option_students_attributes: [{student_id: 1, option_id: 1}])

The console outputs a rollback:

(0.2ms)  BEGIN
  Student Load (0.2ms)  SELECT  "students".* FROM "students" WHERE "students"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Option Load (0.1ms)  SELECT  "options".* FROM "options" WHERE "options"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
   (0.2ms)  ROLLBACK

It does not create the report nor the option_student object.

But if I just type Report.create and then I write

Report.update(1, option_students_attributes: [{student_id: 1, option_id: 1}])

It successfully creates the option student when updating the report. What I am doing wrong with this? I just used nested attributes with other models and it worked.


Solution

  • I am assuming you are using rails 5, which changed the belongs_to relation as by default required. Which theoretically when saving nested attributes should not be a problem, but because the report-id is not yet set when saving (actually: when validating), the save will fail. This can simply be cured by telling rails how associations are related:

    has_many :option_students, inverse_of: :report
    

    Alternatively you could add optional option in the OptionsStudent class:

    belongs_to :report, optional: true 
    

    which is not as correct, it will just skip the validation, but maybe it could be relevant for the other two relations --if either the student or option is not always required.