ActiveRecord exclude results for all queries

activerecord, ruby, ruby-on-rails

Solution

Yes, you can set a default scope:

class User < ActiveRecord::Base
  default_scope where("status != 'Inactive'")
end

User.all # select * from users where status != 'Inactive'

... but you shouldn't.

It will only lead to trouble down the road when you inevitably forget that there is a default scope, and are confused by why you can't find your records.

It will also play havoc with associations, as any records belonging to a user not within your default scope will suddenly appear to belong to no user.

If you had a simple setup with posts and users, and users had a default scope, you'd wind up with something like this:

# we find a post called 1
p = Post.first # <#post id=1>

# It belongs to user 2
p.user_id # 2

# What's this? Error! Undefined method 'firstname' for `nil`!
p.user.first_name

# Can't find user 2, that's impossible! My validations prevent this,
# and my associations destroy dependent records. Can't be!
User.find(2) # nil

# Oh, there he is.
User.unscoped.find(2) <#user id=2 status="inactive">

In practice, this will come up all the time. It's very common to find a record by it's ID, and then try to find the associated record that owns it to verify permissions, etc. Your logic will likely be written to assume the associated record exists, because validation should prevent it from not existing. Suddenly you'll find yourself encountering many "undefined method blank on `nil` class" errors.

It's much better to be explicit with your scope. Define one called `active`, and use `User.active` to explicitly select your active users:

class User < ActiveRecord::Base
  scope :active, -> where("status != 'Inactive'")
end

User.active.all # select * from users where status != 'Inactive'

I would only ever recommend using a `default_scope` to apply an `order(:id)` to your records, which helps `.first` and `.last` act more sanely. I would never recommend using it to exclude records by default, that has bitten me too many times.

Problem

So for some reason, my client will not drop inactive users from their database. Is there a way to globally exclude all inactive users for all ActiveRecord calls to the users table? EX: `User.where("status != 'Inactive'")` I want that to be global so I don't have to include that in EVERY user statement.

Original source