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.