Rails 2.3.8: Fetching objects via ActiveRecord without building all the objects
I'm wondering if there's a way to fetch objects from the DB via ActiveRecord, without having Rails build the whole objects (just a few f开发者_如何学运维ields).
For example, I sometimes need to check whether a certain object contains a certain field. Let's say I have a Student object referencing a Bag object (each student has one bag). I need to check if a female student exists that her bag has more than 4 pencils.
In ActiveRecord, I would have to do something like this:
exists = Student.female.find(:all, conditions => 'bags.pencil_count > 4', :include => :bag).size > 0
The problem is that if there are a 1000 students complying with this condition, a 1000 objects would be built by AR including their 1000 bags.
This reduces me to using plain SQL for this query (for performance reasons), which breaks the AR. I won't be using the named scopes, and I would have to remember to update them all around the code, if something in the named scope changes.
This is an example, but there are many more cases that for performance reasons, I would have to use SQL instead of letting AR build many objects, and this breaks encapsulation.
Is there any way to tell AR not to build the objects, or just build a certain field (also in associations)?
If you're only testing for the existence of a matching record, just use Model.count
from ActiveRecord::Calculations, e.g.:
exists = Student.female.count( :conditions => 'bags.pencil_count > 4',
:joins => :bag
) > 0
count
simply (as the name of the class implies), does the calculation and doesn't build any objects.
(And for future reference it's good to know the difference between :include
and :joins
. The former eager-loads the associated model, whereas the latter does not, but still lets you use those fields in your :conditions
.)
Jordan gave the best answer here - especially re: using joins instead of include (because join won't actually create the bag objects)
I'll just add to it by saying that if you do actually still need the "Student" objects (just with the small amount of info on it) you can also use the :select keyword - which works just like in mysql and means the db I/O will be reduced to just the info you put in the select - and you can also add derived fields form the other tables eg:
students = Student.female.all(
:select => 'students.id, students.name, bags.pencil_count AS pencil_count',
:conditions => 'students.gender = 'F' AND bags.pencil_count > 4',
:joins => :bag
)
students.each do |student|
p "#{student.name} has #{student.pencil_count} pencils in her bag"
end
would give eg:
Jenny has 5 pencils in her bag
Samantha has 14 pencils in her bag
Jill has 8 pencils in her bag
(though note that a derived field (eg pencil_count) will be a string - you may need to cast eg with student.pencil_count.to_i )
精彩评论