Rails Best Practiceのサイトを見ていたら、結構あおり気味なタイトルの記事があった。
default_scope is evil | Rails Best Practices
要約すると、Rails(ActiveRecord)の default_scope
は2つの理由から使うべきではないとのことです。
- default_scopeのオーバライドができない
- モデルをイニシャライズするときにdefault_scopeの副作用が影響する
サンプルコードもあったので自分の環境でも試してみました。
以下の環境で試してみました。
- Rails4.1.5
- Ruby2.1.2
- ベースとなるモデルは以下の様なUserクラスがあると想定(default_scopeを指定)
class User < ActiveRecord::Base
default_scope ->{where(role: "admin").order('created_at ASC')}
end
> User.limit(10)
# SELECT `users`.* FROM `users` WHERE `users`.`role` = 'admin' ORDER BY created_at ASC LIMIT10
- default_scopeのオーバライドができない
> User.order('updated_at DESC').limit(10)
SELECT `users`.* FROM `users` WHERE `users`.`role` = 'admin' ORDER BY created_at ASC, updated_at DESC LIMIT 10
そこで、default_scopeを無効にするために、 unscoped を使う
> User.unscoped.order('updated_at DESC').limit(10)
SELECT `users`.* FROM `users` ORDER BY updated_at DESC LIMIT 10
まー、これについては想定通りだと思う。その名の通り、default_scopeとしてデフォルトの検索条件を指定しているので、無効にしたいときは明示的に無効にしないといけない。
要するにUserモデルのクエリを考えるときは常に default_scope のことを意識しとかないといけない。
- モデルをイニシャライズするときにdefault_scopeの副作用が影響する
> User.new
<User id: nil, name: "", role: "admin", created_at: nil, updated_at: nil>
?! newしただけで、 role
に admin
というデフォルト値が入ってる。
default_scopeで定義している where(role: "admin")
部分が効いている状態です。
ほとんどの人が意図していない挙動かと思います。
default_scope は read する時のみ効果のあるものかとおもいきや、モデルをイニシャライズするときにも値がセットされてしまう。。。
まとめ
個人的には特に 2番目 の 「モデルをイニシャライズするときにdefault_scopeの副作用が影響する」 は想定外の挙動なので、出来る限りdefault_scopeは使用しない方が賢明なのかなと思いました。
使用する場合も、意図せず値が設定されないように、 default_scope に設定する条件は order などのみで、カラム属性に影響しない条件のみ入れるなどを考慮する必要がありそうです。
上記2点の挙動を許容した上で利用する必要があるなと。
Rails Best Practice は結構色んな発見があります!!!