Bazley
Bazley

Reputation: 2847

Rails validation failure with has_many and create during database seed

I'm trying to seed my database but I get a failed validation. It can't find user_id:

Validation failed: Characters user can't be blank

Why is this happening? When you do self.characters.create isn't the user id automagically passed to the character that you're creating?

user.rb

has_many :characters
accepts_nested_attributes_for :characters

after_save do
  if (self.characters.count == 0)
    self.characters.create
  end
end

character.rb

belongs_to :user
validates :user_id, presence: true

seeds.rb

User.create!(email: "[email protected]",
             activated_at: Time.zone.now,
             characters_attributes: [{ first_name: "Baz", last_name: "Chump" }])

Upvotes: 2

Views: 166

Answers (1)

Arslan Ali
Arslan Ali

Reputation: 17802

The reason you are getting this error: you can't save a Character object in the database unless it has an associated parent User object.

In your db/seeds.rb file, you are trying to create Character objects, and then User object, but a Character object needs a valid user_id.

Solution:

You need to use inverse_of to tell explicitly Rails that Character objects dependant upon User object.

has_many :characters, inverse_of: :user

And in your Character model:

belongs_to :user, inverse_of: :characters

The validation in Character model should be like following:

validates :user, presence: true # It will server the same purpose as does `user_id`

Edit:

The problem doesn't lie in after_save. The problem exists in the following line:

User.create!(email: "[email protected]",
             activated_at: Time.zone.now,
             characters_attributes: [{ first_name: "Baz", last_name: "Chump" }])

Through this code, Rails will first create a Character object with the attributes: { first_name: "Baz", last_name: "Chump" }, but since a Character object must have an associated User object, so Rails will fail to create a Character object, stating that Characters user can't be blank.

Putting inverse_of makes Rails create a User object first, and then using its id, create the subsequent Character objects.

Upvotes: 1

Related Questions