何旭东chucai的专栏

Web开发实录:Ruby, Rails, Java, Coffeescript , Sass, Html, JQuery

Rails ActiveRecord的default_scope的坑

Rails ActiveRecord的default_scope的坑

其实,我是特别反对使用default_scope的。它很强大,同时也非常的难于驾驭。它的优点在于我们只需要在Model层增加一行代码,就能解决整个项目中的如何一个地方数据的获取。

举个例子,我们有一个项目的Model,前期比较简单,我们在代码的如何地方都可以方便的使用Project.find 或者 Project.where...。然后,我们来了一个新的需求,需要给Project增加一个开关,只有通过审核的项目才能在项目中显示。这个时候,我们首先想到的方案就是使用 default_scope。 So easy! 我们只需要在Model中增加一行代码 default_scope { where("checked is not null") }。 表面上,或者没有经过全面的测试,这行代码没有问题。而且非常方便的解决了我们的问题。但其中隐藏很多问题。

问题

会在系统的其他引用Project的地方报 nil 异常

比如我们有一个User 模型, 它跟 Project的关系是 belongs_to的关系。所以,下面的代码

@users.each do |user|
    user.project.name
end

是比较常见的case。 但由于projectdefault_scope的原因,导致 projectnil, 这个时候,就会报错~

解决办法:每一个引用project的地方,都做nil判断。 这个解决方案不好,特别麻烦。一种比较好的方案是引入 gem 'unscoped_associations'.

组合而成的SQL会有问题~

这个问题比较隐晦,很难发现。我先用一个例子说明,接着上面说的。
1. 有一个新的模型 Investor,它跟Project一样,有同样的default_scope的条件,也就是说在 investors 表 和 projects 表,都有一个checked字段。
2. 约会的模型 Interview会关联上 ProjectInvestor
现在有一个需求,客户需要获取所有的Interview。我们一般会写如下的代码

Investor.includes(:project, :investor).all

上面这行代码会报错!原因是框架在组合SQL的时候,会有两个checked字段的条件,如果我们使用default_scope { where("checked is not null") } 这种方式,框架是不能设置 projects.checked 这样的条件(如果使用Hash方式,是可以智能的组合的,所以,尽量要使用hash).

解决办法:通过arel 底层组合SQL

default_scope { where(arel_table[:checked].not_eq(nil)) }

总结

default_scope给我们解决的问题的同时,会引入更多的问题。甚至需要修改底层代码才能解决。所以,在使用default_scope的时候,一定要清楚自己做什么。

阅读更多
文章标签: rails
个人分类: ruby on rails
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭