One great thing that ActiveRecord provides is the ability to create named scopes that can be chained, can be used in element collections and do all sorts of cool things!
But one not so cool thing that sometimes appears to be useful, is the definition of the default_scope, that adds that criteria to all queries using that element.
Once upon a time, a couple of weeks ago, I had to implement a “hidden” list of objects, based on a flag in the database, the objects should not appear in the Rails application, and I thought I’ll just create a default_scope with a simple where(flag:0)
Then I tried to build a query a month later, in a different part of the app that had nothing to do with the UI to process some batch jobs, that needed to get all the objects in the list, not only the ones that were not hidden from the UI and spent two hours fighting AR to list everything.
Of course you can use use “unscoped” to solve that, but only the lost hours fighting a bug caused by me is already enough to make me not want to use default scope again.
And it will create lots of weird bugs depending on what you added to the default scope.
For example, you thought that adding a default order to the object was a good idea? think again!
class TestModel < ApplicationRecord default_scope { order('created_at desc') } end TestModel.order('name asc') # TestModel Load (3.3ms) SELECT "test_models".* FROM "test_models" ORDER BY created_at desc, name asc LIMIT ? [["LIMIT", 11]]
With this simple test we just verified the amount of possible bugs for a default_scope/order by.
It can also create problems with your relationships, as we’ll see bellow:
class AnotherTest < ApplicationRecord belongs_to :test_model default_scope( where(published: true) } end class TestModel < ApplicationRecord has_many :another_tests end tm = TestModel.last tm.another_tests # AnotherTest Load (0.7ms) SELECT "another_tests".* FROM "another_tests" WHERE "another_tests"."published" = ? AND "another_tests"."test_model_id" = ? LIMIT ? [["published", 1], ["test_model_id", 1], ["LIMIT", 11]]
Seems to be what you are looking for, until you need to list all unpublished posts from that TestModel, you just think, ok, I’ll use unscoped for it, right? Not so fast!
tm.another_tests.unscoped # AnotherTest Load (0.8ms) SELECT "another_tests".* FROM "another_tests" LIMIT ? [["LIMIT", 11]]
“unscoped” will remove all current scopes, including association scopes.
It will also create problems if we use “dependents: :destroy” for example, because the scope will make rails delete only the published dependents, causing a possible “foreign key violation” in the database, causing the delete to fail.
So, in my opinion, except for some very rare orderings that will never change for your application, you should avoid “default_scope” with all your forces