Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding a Rails default_scope

In Rails 3:

foos = Foo.unscoped.where(:baz => baz)

Short answer: Do not use default_scope unless you really have to. You'll probably be better off with named scopes. With that said, you can use with_exclusive_scope to override the default scope if you need to.

Have a look at this question for more details.


If all you need is to change the order defined in default_scope, you can use the reorder method.

class Foo < ActiveRecord::Base
  default_scope order('created_at desc')
end

Foo.reorder('created_at asc')

runs the following SQL:

SELECT * FROM "foos" ORDER BY created_at asc

Since 4.1 you can use ActiveRecord::QueryMethods#unscope to fight default scope:

class User < ActiveRecord::Base
  default_scope { where tester: false }
  scope :testers, -> { unscope(:where).where tester: true }
  scope :with_testers, -> { unscope(:where).where tester: [true, false] }
  # ...
end

It is currently possible to unscope stuff like: :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having.

But still please avoid using of default_scope if you can. It's for your own good.


You can override a default scope using the with_exclusive_scope method. So:

foos = Foo.with_exclusive_scope { :conditions => ["baz = ?", baz] }
  • with_exclusive_scope documentation

On Rails 5.1+ (and maybe earlier, but I've tested it works on 5.1) it is possible to unscope a specific column, which imho is the ideal solution for removing a default_scope in a fashion that can be used inside a named scope. In the case of the OPs default_scope,

Foo.unscope(where: :bar)

Or

scope :not_default, -> { unscope(where: :bar) }
Foo.not_default

Will both result in a sql query that doesn't apply the original scope, but does apply whatever other conditions get merged into the arel.


Rails 3 default_scope does not appear to get overridden like it did in Rails 2.

e.g.

class Foo < ActiveRecord::Base
  belongs_to :bar
  default_scope :order=>"created_at desc"
end

class Bar < ActiveRecord::Base
  has_many :foos
end

> Bar.foos
  SELECT * from Foo where bar_id = 2 order by "created_at desc";
> Bar.unscoped.foos
  SELECT * from Foo;  (WRONG!  removes the "has" relationship)
> Bar.foos( :order=>"created_at asc" )  # trying to override ordering
  SELECT * from Foo where bar_id = 2 order by "created_at desc, created_at asc"

In my app, using PostgreSQL, the ordering in the default scope WINS. I'm removing all of my default_scopes and coding it in explicitly everywhere.

Pitfall Rails3!