Search code examples
ruby-on-railssql-serverrubyactiverecordruby-on-rails-7

ActiveModel::NestedError attribute=survey_questions.survey, type=blank, options={:message=>:required}


I have the below models in a Ruby on Rails application(I am using Rails 7):

class Survey < ApplicationRecord
  has_many :survey_questions, foreign_key: 'IdSurvey', dependent: :destroy
  accepts_nested_attributes_for :survey_questions

  self.table_name = 'Survey'
  self.primary_key = 'IdSurvey'
end

class SurveyQuestion < ApplicationRecord
  belongs_to :survey, foreign_key: 'IdSurvey', inverse_of: :survey_questions
  has_many :survey_question_options, foreign_key: 'IdSurveyQuestion', dependent: :destroy
  accepts_nested_attributes_for :survey_question_options

  self.table_name = 'SurveyQuestion'
  self.primary_key = 'IdSurveyQuestion'
end

class SurveyQuestionOption < ApplicationRecord
  belongs_to :survey_question, foreign_key: 'IdSurveyQuestion', inverse_of: :survey_question_options

  self.table_name = 'SurveyQuestionOption'
  self.primary_key = 'IdSurveyQuestionOption'
end

While trying to create a new instance of Survey model including nested elements and saving it into the database I am getting the below error:

#<ActiveModel::Errors [#<ActiveModel::NestedError attribute=survey_questions.survey, type=blank, options={:message=>:required}>, #<ActiveModel::NestedError attribute=survey_questions.survey_question_options.survey_question, type=blank, options={:message=>:required}>]>

And here is how I am doing this:

irb(main):066:0> s = Survey.new(Title: 'S', survey_questions_attributes: [{Title: 'Q', survey_question_options_attributes: [{Title: 'O'}]}])
=> 
#<Survey:0x00000226dea17528
...
irb(main):068:0> s.save
=> false
irb(main):069:0> s.errors
=> #<ActiveModel::Errors [#<ActiveModel::NestedError attribute=survey_questions.survey, type=blank, options={:message=>:required}>, #<ActiveModel::NestedError attribute=survey_questions.survey_question_options.survey_question, type=blank, options={:message=>:required}>]>

Solution

  • You need inverse_of on both sides of the association to avoid the belongs_to being considered blank when saving nested attributes

    class Survey
      has_many :survey_questions, inverse_of: :survey
    end
    
    class SurveyQuestion
      belongs_to :survey, inverse_of: :survey_questions
      has_many :survey_question_options, inverse_of: :survey_question
    end
    
    class SurveyQuestionOption
      belongs_to :survey_question, inverse_of: :survey_question_options
    end