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

jjym · 2012年08月21日 · 最后由 xds2000 回复于 2012年09月07日 · 4319 次阅读

之前在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来取值,这时查询才会发生!

共收到 7 条回复

好文没人支持啊

结论还没说出本质啊

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

写个简单的版本出来啊

支持继续补充啊。

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

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

好文。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册