Reputation: 728
Example scenario:
The model Worker belongs_to
the 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 case of the where
function, passing an instance such as Bucket.first
works in lieu of the exact id
. So one would think it'd work for the new
function too. Instead it fails silently!
Why does it work this way?
Upvotes: 3
Views: 1836
Reputation: 16287
I believe this is happening in the ActiveRecord::PredicateBuilder. You can see there if the value is an ActiveRecord::Base object it will call id
on it. The new
method does not trigger this code so it will behave differently.
I prefer to be explicit and pass in the id
directly. However upcoming in Rails 4 you will be able to do this:
Worker.where(bucket: Bucket.first).count
That nicely parallels with initialization:
Worker.new(bucket: Bucket.first)
In general, I recommend passing an id
if the attribute you are setting/matching ends in _id
.
Update: I also wanted to point out, the initialization will inherit the where
conditions. So this will work in Rails 3.2:
Worker.where(bucket_id: Bucket.first).new
I'm assuming that ends up going through the PredicateBuilder, but not certain about that. Even though this works I don't recommend it.
Upvotes: 3
Reputation: 1102
From what I understand, Bucket.first
will return an instant object, but Bucket.first.id
will only return id of the instant object.
So, when you creating Worker.new
and pass bucket_id: Bucket.first
, it will actually do not work, on the other hand,
Worker.new bucket_id: Bucket.first.id
it will pass the correct param in order to create new Worker.
Upvotes: 0
Reputation: 96614
When you use .new
you are creating a blank record but it has no ID yet. With Active Record this is frequently used for forms.
This is different from say .create
which, if given the required valid parameters will create a record with an ID. Similarly, selecting a record with Bucket.first
gets an existing record that has an ID.
If you use .new
, set the required parameters and then do a .save
you will then get an ID.
Upvotes: 0