9

我在 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
4

6 回答 6

8

好吧,我认为您走在正确的轨道上,但也许只是将其作为 Foo 模型中的一种方法

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

然后只是说,Foo.first.children?并获取trueFoo 实例是否有任何孩子。

于 2013-09-04T22:01:51.020 回答
6

这是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都会进行 DB 调用,any?如果spaces总是加载到内存中,则不会。现在正如我所说,很多时候,重要的不是数据库调用的效率(是的,SQL 调用exists?效率更高),但any?不一定会调用数据库,这是一个巨大的事实优势。寻找自己:

[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

现在,正如我所说,很多时候,这取决于具体情况——您可能在很多情况下这些项目没有加载到内存中,但很多时候,它们是。根据您如何称呼它,选择最适合您的。

于 2013-09-04T22:17:38.570 回答
5

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

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
于 2013-09-04T22:43:47.877 回答
0

您可以继承 Thing Item 和 Widget。或者添加一个多态连接表来跟踪它。不理想,我知道。

你至少可以这样做,所以它会读起来更好一些。

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

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

于 2013-09-04T22:05:26.990 回答
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?

更正确,并且已经在我面前提出。

于 2013-09-04T22:40:03.697 回答
0

@Marcelo De Polli 的答案是迄今为止发布的最普遍的答案。这个答案是 Rails 5 的更新版本。

模型的父类ApplicationRecord在 Rails 5 及更高版本中,过去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
于 2021-06-25T11:05:27.367 回答