Reputation: 2457
I have a relation between two objects. Let's say it like this: Model1 has_many Model2 (That doesn't really matter)
And say, I want to filter-out some of the results:
a = Model1.find(123)
b = a.model2
And now, for example, I want to select only EVEN records (by ID)
If I do following: b.select {|x| x.id % 2 == 0}
then it returns all even records as expected. And NO additional database queries created.
But if I define a class method in the Model2:
def self.even_records
select {|x| x.id % 2 == 0}
end
Then, for some magic reason it makes an additional query to database, that looks like it re-instantiated the "b" variable (re-loads the relation):
Model2 Load (0.4ms) SELECT `model2`.* FROM `model2` WHERE `model2`.`model1_id` = 123
Why it behaves so ? Is there any way I can fix it ?
P.S I have no fishy callbacks, like after_find
or whatsoever defined in any of models.
Upvotes: 1
Views: 353
Reputation: 3866
The Basic difference between these two is that when you call select method on b which is an array, than it calls the enumerable method select.
b.select {|x| x.id % 2 == 0}
and when you write in a method, it calls the select method of activerecord query interface.
def self.even_records
select {|x| x.id % 2 == 0}
end
BTW Ruby have methods like even?
and odd?
, so you can directly call them :
even_records = b.select{|x| x.id.even?}
odd_records = b.select{|x| x.id.odd? }
Edit:
I found a simple solution for you, you can define a scope in your model Model2
like below,
scope :even_records, -> { where ('id % 2 == 0') }
and now if you will call :
Model2.even_records
you will have your even_records. Thanks
Upvotes: 1
Reputation: 51161
ActiveRecord
scopes are evaluated lazily, i.e. scope is evaluated when its result is necessary. When you try this code in console, inspect
method are called implicitly on every evaluated object, including ActiveRecord::Relation
instance returned from
b = a.model2
call. After calling inspect
on ActiveRecord::Relation
, scope is evaluated and DB query is created since it's necessary to show inspect
return value properly.
On the contrary, when you run your code outside rails console,
b = a.model2
won't produce DB query, thus there will probably be only one database query.
Upvotes: 2