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的時候,一定要清楚自己做什麼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章