I'm working in Rails 3 and have a table with multiple child tables, e.g.
class Foo < ActiveRecord::Base
has_many :things
has_many :items
has_many :widgets
end
class Thing < ActiveRecord::Base
belongs_to :foo
end
class Item < ActiveRecord::Base
belongs_to :foo
end
class Widget < ActiveRecord::Base
belongs_to :foo
end
Is there a simple way for me to check to if a given Foo has a child record in one or more of the tables? Basically, is there a better way to do this:
if !foo.things.empty? or !foo.items.empty? or !foo.widgets.empty?
puts "This foo is in use!"
emd
Well, I think you're on the right track, but maybe just put that as a method in your Foo model
class Foo < ActiveRecord::Base
def children?
things.any? || items.any? || widgets.any?
end
end
Then just say, Foo.first.children?
and get true
if the Foo instance has any children.
This should work for any given model.
class Foo < ActiveRecord::Base
def children?
has_associated_records = self.class.reflect_on_all_associations.map { |a| self.send(a.name).any? }
has_associated_records.include?(true)
end
end
This is what any?
is for.
class Foo < ActiveRecord::Base
def children?
things.any? || items.any? || widgets.any?
end
end
Since this has become a topic of contention, I present to you:
> foo = Foo.last
Foo Load (0.6ms) SELECT "foos"......
> foo.items
Item Load (0.9ms) SELECT "items".*.......
> foo.items.any?
=> true #OH, WHAT's that? NO SQL CALLS? GEE WILLICKERS
> foo.items.exists?
Item Exists (0.5ms) #Hmmmmmm....
=> true
The point here is that under any circumstances, exists
makes a DB call, where as any?
will not, if spaces
is always loaded into memory. Now as I said, many times, the importance is not the efficiency of the DB call (AND YES, the SQL call exists?
makes is more efficient), but the fact that any?
won't necessarily make a call to the DB, which is a HUGE advantage. Look for yourself:
[20] pry(main)> Benchmark.measure { foo.item.exists? }
Item Exists (0.5ms) SELECT 1 AS one FROM "items" ...
=> #<Benchmark::Tms:0x007fc1f28a8638
@cstime=0.0,
@cutime=0.0,
@label="",
@real=0.002927,
@stime=0.0,
@total=0.00999999999999801,
@utime=0.00999999999999801>
[21] pry(main)> Benchmark.measure { foo.items.any? }
=> #<Benchmark::Tms:0x007fc1f29d1aa0
@cstime=0.0,
@cutime=0.0,
@label="",
@real=7.6e-05,
@stime=0.0,
@total=0.0,
@utime=0.0>
For a more concise timing, look at this:
> Benchmark.measure { 1000.times {foo.items.exists?} }.total
=> 2.5299999999999994
> Benchmark.measure { 1000.times {foo.items.any?} }.total
=> 0.0
Now as I said, many times, it depends on circumstance -- you could have many circumstances where these items aren't loaded into memory, but many times, they are. Choose which one works best for you depending on how you're calling it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With