Home > Net >  Rails Has-Many Relationship: Create not saving records into the database?
Rails Has-Many Relationship: Create not saving records into the database?

Time:10-29

If I were to assume the models:

class User < ActiveRecord::Base
 has_many :posts
end

class Post < ActiveRecord::Base
 belongs_to :user
end

When I'm trying to run User.first.posts.create [attributes], the model gets created, but its id is nil and is not saved in the database. Could anyone explain why? I thought this sort of behaviour was expected from #new, not from #create.

CodePudding user response:

Your expectations are wrong.

Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.

The implementaton is actually dead simple:

def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    object = new(attributes, &block)
    object.save
    object
  end
end

So for example when you do:

post = Post.create(title: 'Not a valid post')

This will always return an instance of post. If you then inspect the object you can see that its not persisted and the errors object will tell you why:

post.valid? # false
post.persisted? # false
post.errors.full_messages # ["User can't be blank."]

If you create the record off an association you're also mutating it:

user = User.first
post = user.posts.create(attributes)
user.posts.to_a.include?(post) # true

This might seem like undesirable behavior but is needed for nested attributes to work properly.

Use create! (which raises an error if the record is invalid) instead in non-interactive contexts (like seed files) where creating a record should not be expected to fail. Don't use it your controllers where is invalid input is not an exceptional event - and where its just a crutch akin to throwing a bunch of log or puts statements all over the code. Write tests that cover invalid and valid input instead.

If you really need to use a debugger like byebug or pry to step into the controller action and inspect the model instance.

  • Related