Rails [新手向] ActiveRecord::Base 与 ActiveRecord::Relation 关系及基本原理

jjym · August 21, 2012 · Last by xds2000 replied at September 07, 2012 · 5517 hits

之前在 github 上看 Rails 源码真的要疯了。。现在有 rubymine 这个神器。看源码方便多了。。

正题:

AR = ActiveRecord

AR::Base 和 AR::Record 的关系

在 AR::Base 上调用查询方法如 where,all,limit 等均会代理到 scoped 方法,而 scoped 方法大意就是会返回一个 AR::Relation 的实例,AR::Base 本身拥有一个 AR::Relation 实例,并且之后所有查询都在此 relation 上 clone 而来。所以我们在 Base 上调用的查询接口实际是 Relation 中的。

补充:前几天看 Kaminari 源码,发现仅在 Base 上 include 了分页方法的模块,经人指点后发现这是因为 Relation 新建时会接受一个 Base 的引用,并且 Relation 的 method_missing 会调用该引用的方法。所以在 Base 上定义就 OK。


延迟执行很神奇吗?

其实没什么神奇的,原理很简单(实现还是有困难的。。),每次把条件存下,然后真正查询的时候再调用

比如

def uniq(value = true)
      relation = clone
      relation.uniq_value = value
      relation
end

存下条件,返回 relation 这样就能进行链式调用了

真正查询时在根据条件生成查询语句


什么时候查询?

打开 rails c

输入Post.where "id > 5"

生成SELECT "posts".* FROM "posts" WHERE (id > 5)

输入Post.where("id > 5").first

生成SELECT "posts".* FROM "posts" WHERE (id > 5) LIMIT 1

where 做的应该只是保存查询条件才对。现在居然生成查询语句,这不科学!!

def where(opts, *rest)
      return self if opts.blank?

      relation = clone
      relation.where_values += build_where(opts, rest)
      relation
end

你没猜错!返回的的确是 relation,所以答案就在这个坑跌的 relation 中

AR::Relation#inspect

def inspect
      to_a.inspect
end

因为 irb 每次执行都会输出返回值的 inspect,所以造成了 where 语句执行查询的错觉。

在 Rails 中我们通常调用 each 来遍历,或者 first/last 来取值,这时查询才会发生!

好文没人支持啊

结论还没说出本质啊

#2 楼 @hooopo 大致看了下源码..没太深入本质,求指点

写个简单的版本出来啊

支持继续补充啊。

“因为 irb 每次执行都会输出返回值的 inspect,所以造成了 where 语句执行查询的错觉。” 趟过这个地雷的就会有经验了,另外 sql 的 log 默认不会打印到控制台。

恩,以前也好奇过,然后看了下源码,明悟了

You need to Sign in before reply, if you don't have an account, please Sign up first.