0

Ruby 和 Rails 的新手,如果我的术语有点不对,请原谅我。

我正在优化一些继承的代码并查看日志,因为 .rabl 文件中有这样的行,所以我看到查询重复自己:

node(:program) { |user| (!user.programs.first.nil?) ? user.programs.first.name : ''  }

user并且program都是活动记录对象

转到 rails 控制台,我可以复制问题,但也可以获得预期的行为,这只是一个查询:

>u = User.find(1234)
 User Load (0.3ms)  SELECT `users`.* FROM `users` WHERE [...]
> (!u.programs.first.nil?) ? u.programs.first.name : ''
 Program Load (0.2ms)  SELECT `programs`.* FROM `programs` [...]
 Program Load (0.3ms)  SELECT `programs`.* FROM `programs` [...]
=> "Output"

请注意,在控制台中重复三元语句总是会给我 2 个查询。

我可以得到这样的预期行为:

> newu = User.find(12345)
  User Load (3.8ms)  SELECT `users`.* FROM `users` WHERE [...]
> newu.programs
  Program Load (0.6ms)  SELECT `programs`.* FROM `programs` [...]
> (!newu.programs.first.nil?) ? newu.programs.first.name : ''
=> "Output"

现在重复三元语句根本不会重新查询。

所以问题是:为什么调用newu.programs会改变行为?不应该调用u.programs.first.nil?也以相同的方式加载所有程序记录吗?

4

1 回答 1

1

有了联想,first就不是糖了[0]

如果关联已加载,则它只返回数组的第一个元素。如果未加载,它会进行数据库查询以仅加载该元素。它不能把它粘在关联缓存中(至少不是更聪明),所以下一个查询first再次执行查询(如果打开,这将使用查询缓存)

Rails 的假设是,如果关联很大,并且您只使用其中的一个元素,那么加载整个内容将是愚蠢的。如果不是这种情况并且您只使用一个项目,但您正在重复使用它,这可能会有点烦人。

为避免这种情况,您可以将项目分配给局部变量,以便您真正只调用first一次,或者执行

newu.programs[0]

这将加载整个关联(仅一次)并返回第一个元素。

Rails 对include?. 它不会加载整个集合,而是运行一个查询来测试特定项目是否在集合中(除非集合已加载)

于 2013-05-31T17:19:37.057 回答