When is passing an instance instead of id done in Ruby on Rails ActiveRecord?
Example scenario: Model Worker belongs_to
model bucket.
See the following queries:
1.9.3p194 :045 > Worker.where(bucket_id: Bucket.first).count
(0.7ms) SELECT COUNT(*) FROM "workers" WHERE "workers"."bucket_id" = 1
=> 38
1.9.3p194 :046 > Worker.where(bucket_id: Bucket.first.id).count
(0.7ms) SELECT COUNT(*) FROM "workers" WHERE "workers"."bucket_id" = 1
=> 38
1.9.3p194 :047 > Worker.new bucket_id: Bucket.first
=> #<Worker id: nil, email: nil, created_at: nil, updated_at: nil, bucket_id: nil>
1.9.3p194 :048 > Worker.new bucket_id: Bucket.first.id
=> #<Worker id: nil, email: nil, created_at: nil, updated_at: nil, bucket_id: 2>
As you can see, in the case of a function, where
passing an instance like this Bucket.first
works instead of an exact one id
. So one would think it would work for a function as well new
. Instead, it fails!
Why does it work?
source to share
I believe this is happening in ActiveRecord :: PredicateBuilder . You can see if this value is an ActiveRecord :: Base object it calls it id
. The method new
does not run this code, so it will behave differently.
I prefer to be explicit and pass directly to id
. However upcoming in Rails 4 you will be able to do this:
Worker.where(bucket: Bucket.first).count
This goes well with initialization:
Worker.new(bucket: Bucket.first)
In general, I recommend passing id
in if the attribute you are setting / mapping ends with _id
.
Update: I also wanted to point out, initialization inherits conditions where
. So this will work in Rails 3.2:
Worker.where(bucket_id: Bucket.first).new
I am guessing what ultimately happens through PredicateBuilder, but not sure about it. Even though it works, I don't recommend it.
source to share
From what I understand, Bucket.first
will return an instant object, but Bucket.first.id
will only return the id of the instant object.
So when you build Worker.new
and go through bucket_id: Bucket.first
, on the other hand, it won't work,
Worker.new bucket_id: Bucket.first.id
it will accept the correct parameter to create a new Worker.
source to share
When you use .new
, you create an empty entry, but it doesn't have an ID yet. In Active Record, this is often used for forms.
This is in contrast to say .create
, which, if the required valid parameters are given, will create a record with an id. Likewise, selecting a record with Bucket.first
gets an existing record with an identifier.
If you are using .new
, set the required parameters and then execute .save
and then get the ID.
source to share