Why using default_scope is a bad idea
default_scope
is a method provided by ActiveRecord, which allows you to set
a default scope (as its name implies) for all operations done on a given model.
It can be useful for allowing soft-deletion in your models, by having a
deleted_on
column on your model and setting the default scope to
deleted_on: nil
class Animal
default_scope where(deleted_on: nil)
end
This will hide deleted records and only return non-deleted
ones in your queries after setting deleted_on
instead of
calling destroy
.
> Animal.limit(5)
Animal Load (4.2ms) SELECT `animals`.* FROM `animals` WHERE `animals`.`deleted_on` IS NULL LIMIT 5
Quite useful! However, default_scope
has a dangerous behavior:
it affects your model’s initialization.
Say you were using STI on your Animal
model, and you had a default_scope
for always filtering your model by “Cat”:
class Animal
default_scope where(type: "Cat")
end
When you initialize a new Animal, it will always use the value defined in your default scope:
> Animal.new
=> #<Animal id: nil, created_at: nil, updated_at: nil, type: "Cat">
This can lead to some headaches when you’re not aware of this side-effect, and depending on the default scoped attribute/value it can cause some pretty bad bugs.
Also, I’d like to mention the fact that it’s very difficult to write queries
once you have used default_scope
on a model. For example:
Animal.where('deleted_on is not null')
won’t work, you’d need to use
Animal.unscoped
, which makes everything awkward. You also need to use
unscoped
carefully, because it will remove all scopes of the relation, not just
the default scope.
I recommend that you always avoid default scope if possible. Prefer explicit scopes instead. If you really need to use it, use it with care.