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