LoginSignup
34
35

More than 5 years have passed since last update.

Railsのdefault_scopeをどうしても使いたい時

Last updated at Posted at 2016-04-16

サマリ

  • default_scopeは便利
  • default_scopeは安直につかうとメンテ性が悪くなる
  • それでもdefault_scopeを使いたい

というときに、メンテ性を下げずにdefault_scopeを使う

(環境: Rails 4.2.5.1)

前提

default_scopeについて

ActiveRecordには、すべてのクエリに追加で絞り込みやorderを指定する default_scope という機能がある。

例えば、以下の例では、「disabled:trueなitemは常に除外する」という用例。

# app/models/item.rb
class Item < ActiveRecord::Base
  default_scope { where(disabled: false) }
end

# itemsテーブルに id: 1, disabled: true なデータがあるとき
Item.find(1) # raises ActiveRecord::RecordNotFound

default_scopeが良くないとされる理由

最初どんなに「disabledなデータは絶対にクエリ検索結果から除外して大丈夫だ」と思っていたとしても、それは最初だけです。
例えば、管理画面からの呼び出しでは、disabledのチェックをつけ消しする必要があり、そうするとdisabledなデータも返さなければいけなくなります。

そのようなときは一応 unscope を使うとdefault_scopeを解除することができます。が、いかにもスパゲッティーコードが作られる臭いしかしませんね。

詳しくは触れませんが、http://stackoverflow.com/questions/25087336/why-is-using-the-rails-default-scope-often-recommend-against
での回答は的を射ていると思います。

それでもdefault_scopeを使いたい理由

defaultではない通常のscopeを使うことももちろん可能。

class Item < ActiveRecord::Base
  scope :active, -> { where(disabled: false) }
end

しかし、最初の例では、管理画面以外のすべてのコードで disabledなItemの存在は消したいと考えていて、もしかしたら95%以上のコードではこのscopeを使うかもしれない。
そうすると毎度scopeをつけるのは面倒だったり、つけ忘れる危険性がある。

対処法

Itemにはdefault_scopeを適用せず、
新しいモデル ActiveItem を作り、そこにdefault_scopeを適用する。

その上で、最初の例で言えば、
管理画面からはItem、それ以外のほとんどすべてのコードではActiveItemを使う。

ActiveItem の作り方は以下の2つ

1. 普通にActiveRecord::Baseを継承し、テーブル名を指定

class ActiveItem < ActiveRecord::Base
  self.table_name = 'items'
  default_scope { where(disabled: false) }
end

2. Itemを継承

追記: これだとSingle Table Inheritanceの扱いになってしまうので別の方法をとる必要がある
(コメント欄)
後ほどきちんと調査して修正します。

class ActiveItem < Item
  default_scope { where(disabled: false) }
end

1と2どっちがいいのか?

ドメインモデル的に考えると、継承する必要はないので、1のほうが良いと思う。
一方でDBとつなぐメソッドはだいたい両方で使いたくなる気がするので、継承したほうが楽。
DBとつなぐメソッドが多い or むしろドメインモデルはActiveRecordと別に分ける(最もかっちり) 場合は2を採用すれば良いかも。

34
35
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
35