Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: How can I tell if a record has a child record in any one of its child tables?

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
like image 879
abeger Avatar asked Sep 04 '13 21:09

abeger


3 Answers

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.

like image 187
Abram Avatar answered Sep 24 '22 23:09

Abram


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
like image 29
Marcelo De Polli Avatar answered Sep 26 '22 23:09

Marcelo De Polli


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.

like image 29
varatis Avatar answered Sep 24 '22 23:09

varatis