なぜレールdefault_scopeを使用することがしばしば推奨されないのですか?


125

インターネットの至る所 レールを使用することdefault_scopeは悪い考えであり、default_scopestackoverflowの上位ヒットはそれをどのように上書きするかということです。これはめちゃくちゃに感じられ、明確な質問に値する(私は思う)。

それで、なぜdefault_scope推奨されているレールを使用しないのですか?

回答:


192

問題1

基本的な例を考えてみましょう:

class Post < ActiveRecord::Base
  default_scope { where(published: true) }
end

デフォルトにする動機は、published: true非公開(非公開)投稿を表示したいときに明示する必要があることを確認することです。ここまでは順調ですね。

2.1.1 :001 > Post.all
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."published" = 't'

まあこれは私たちが期待するものとほとんど同じです。今試してみましょう:

2.1.1 :004 > Post.new
 => #<Post id: nil, title: nil, published: true, created_at: nil, updated_at: nil>

そして、デフォルトのスコープに最初の大きな問題があります:

=> default_scopeはモデルの初期化に影響します

そのようなモデルの新しく作成されたインスタンスでは、default_scopeが反映されます。そのため、未公開の投稿を偶然にリストしないようにしたかったかもしれませんが、現在はデフォルトで公開済みの投稿を作成しています。

問題2

より複雑な例を考えてみましょう:

class Post < ActiveRecord::Base
  default_scope { where(published: true) }
  belongs_to :user
end 

class User < ActiveRecord::Base
  has_many :posts
end

最初のユーザーの投稿を取得できます:

2.1.1 :001 > User.first.posts
  Post Load (0.3ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."published" = 't' AND "posts"."user_id" = ?  [["user_id", 1]]

これは予想通りです(user_idに関する部分を表示するには、右端までスクロールしてください)。

ここで、すべての投稿のリストを取得したい-非公開が含まれている-ログインしたユーザーのビューについて言ってください。の効果を「上書き」または「元に戻す」必要があることに気付くでしょうdefault_scope。簡単なグーグルの後、あなたはおそらくについて知るでしょうunscoped。次に何が起こるかを見てください:

2.1.1 :002 > User.first.posts.unscoped
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"

=>スコープなしでは、通常、選択に適用される可能性のあるすべてのスコープが削除されます。

のさまざまな効果を上書きする方法は複数ありますdefault_scope。これを正しく行うことは非常に迅速に複雑default_scopeなります。そもそもを使用しない方が安全な選択だと私は主張します。


2
積み重ねる:default_scopeが便利だと思ったのは、デフォルトでいくつかのアソシエーションを熱心にロードしたいときだけです。default_scope {eager_load([:category、:comments])}。しかしながら!!!Product.countのようなこのモデルでcountクエリを実行している場合、すべての製品のeager_loadアソシエーションになります。5万件のレコードがある場合、カウントクエリは15ミリ秒から500ミリ秒になりました。必要なのはカウントだけですが、default_scopeは他のすべてに結合したままになるためです。
konung

16
私には、問題は問題#2 unscopedではなく問題であるようですdefault_scope
キャプテンフォジェッティ

4
@CaptainFogetti確かに。default_scopeの不利な点として、スコープなしの欠点を提示するのは良い考えだと私はまだ思います。ほとんどの場合、default_scopeを使用すると、スコープなしを使用する必要があります。これは2次学位の警告(より良い用語がない)であり、メソッドを研究するときに見落としがちです。
wrtsprt

1
回答のユースケースの問題は、未公開の投稿を見つけたい場合が多いということです。実際、公開された投稿を見つけることは特別なケースだと私は主張します。投稿を公開したいのは、誰かがパブリックページを表示しているときだけです。ただし、未公開の投稿を表示したい場合はよくあります。
Bセブン

3
default_scope何かをソートしたいときは、次のように使うのがいいと思います:default_scope { order(:name) }
user2985898

9

未使用のもう一つの理由は、default_scopeあなたが持つ多くの関係を1持っているモデルのインスタンスを削除しているときであるdefault_scopeモデルを

例を考えてみましょう:

    class User < ActiveRecord::Base
      has_many :posts, dependent: :destroy
    end 

    class Post < ActiveRecord::Base
      default_scope { where(published: true) }
      belongs_to :user
    end

を呼び出すuser.destroyと、の投稿はすべて削除されますが、の投稿publishedは削除されませんunpublished。したがって、データベースには、削除するユーザーを参照するレコードが含まれているため、外部キー違反がスローされます。


6

default_scopeは、結果セットを制限するために誤って使用されることがあるので、しばしば推奨されません。default_scopeの適切な使用法は、結果セットを順序付けることです。

wheredefault_scopeでの使用は避け、むしろそのスコープを作成します。


1
2つ目の問題「スコープなしでは、選択に通常適用される可能性のあるすべてのスコープが削除されます(関連付けを含むが、それに限定されない)」は、default_scopeのみが含まれている場合でも依然として存在しますorder。のこの動作unscopedは非常に予想外です。
Zack Xu

1

私にとってはあるではない悪い考えではなく、慎重に使用する必要があります!フィールドが設定されていると、特定のレコードを常に非表示にしたい場合があります。

  1. 好ましくはdefault_scopeDBのデフォルト値と一致する必要があります(例:{ where(hidden_id: nil) }
  2. それらのレコードを表示することを完全に確信している場合は、常にunscopedあなたを回避する方法がありますdefault_scope

したがって、それは実際のニーズによって異なります。


0

私は見つけるdefault_scopeだけであることをいくつかのパラメータを注文するのに有用であることがascまたはdesc全ての状況でオーダー。そうでなければ、ペストのようにそれを避けます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.