Rails:如何判断一条记录在其子表中是否有子记录?

时间:2013-09-04 21:57:55

标签: ruby-on-rails-3 rails-activerecord

我正在使用Rails 3,并且有一个包含多个子表的表,例如

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

我是否有一种简单的方法可以检查给定的Foo是否在一个或多个表中有子记录?基本上,有更好的方法来做到这一点:

if !foo.things.empty? or !foo.items.empty? or !foo.widgets.empty?
  puts "This foo is in use!"
emd

6 个答案:

答案 0 :(得分:8)

嗯,我认为你走的是正确的轨道,但也许只是把它作为你的Foo模型中的一种方法

class Foo < ActiveRecord::Base
  def children?
    things.any? || items.any? || widgets.any?
  end
end

然后如果Foo实例有任何子节点,只需说Foo.first.children?并获取true

答案 1 :(得分:5)

这是any?的用途。

class Foo < ActiveRecord::Base
  def children?
    things.any? || items.any? || widgets.any?
  end
end

由于这已经成为争论的话题,我将向您呈现:

> 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

这里的要点是,在任何情况下,exists都会进行数据库调用,而any?则不会,如果spaces始终加载到内存中。现在正如我所说,很多时候,重要的不是数据库调用的效率(AND YES,SQL调用exists?使得效率更高),但any?不一定会产生这样的事实对DB的调用,这是一个巨大的优势。找你自己:

[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>

有关更简洁的时间,请看:

> Benchmark.measure { 1000.times {foo.items.exists?} }.total
=> 2.5299999999999994
> Benchmark.measure { 1000.times {foo.items.any?} }.total
=> 0.0

正如我所说的那样,很多时候,这取决于环境 - 你可能有很多情况下这些物品没有加载到内存中,但很多时候,它们都是。根据您的调用方式,选择最适合您的方式。

答案 2 :(得分:3)

这适用于任何给定的模型。

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

答案 3 :(得分:0)

你可以继承Thing Item和Widget。或者添加多态联接表以跟踪它。我知道,这并不理想。

你至少可以这样做,所以它会读得更好。

if foo.things.exists? || foo.items.exists? || foo.widgets.exists?
  puts "This foo is in use!"
end

“空?使用'存在?'在幕后,我相信。

答案 4 :(得分:0)

假设所有关联都加载到内存中:

class Foo < ActiveRecord::Base
  has_many :things
  has_many :items
  has_many :widgets

  def in_use?
    [things, items, widgets].flatten.any?
  end
end

修改

我刚才意识到这是错误的:每个关联(即使仍然加载到内存中)都会被加载,这是不好的。

things.any? || items.any? || widgets.any?

更正确,并已在我之前提出。

答案 5 :(得分:0)

@Marcelo De Polli 的答案是迄今为止发布的最概括的答案。 此答案是 Rails 5 的更新版本。

模型的父类在 Rails 5 及更高版本中是 ApplicationRecord,它曾经是 ActiveRecord::Base 直到 Rails 4(注意,原始问题被标记为 Rails 3)。

为了代码的简单性,使用:

class Foo < ApplicationRecord
  def children?
    self.class.reflect_on_all_associations.map{ |a| self.send(a.name).any? }.any?
  end
end

当一个模型可能有很多子类时,要追求更高的运行时效率,请使用:

class Foo < ApplicationRecord
  def children?
    self.class.reflect_on_all_associations.each{ |a| return true if self.send(a.name).any? }
    false
  end
end