Posted 15 days ago. Visible to the public. Repeats.

How to negate scope conditions in Rails

Rails does not offer a "vanilla" way of negating an ActiveRecord scope. Here are two ways to do it yourself without too much effort.

Example:

Copy
class User scope :admins, -> { where(role: ['admin', 'superuser'] } # ... end

Now what if we want a scope of users that are not admins? While you could declare a second scope like scope :non_admins, -> { where(role: ['guest', 'editor']) }, there are ways to use the opposite of the admins scope.

Option A: Subquery

You could just use a subquery. Doing that with scopes is easy:

Copy
User.where.not(id: User.admins)

Rails will generate a query like the following.

Copy
SELECT * FROM users WHERE id NOT IN (SELECT id FROM users WHERE role IN ('admin', 'superuser'))

Note that subqueries may be inefficient on large tables.

Option B: Use where_values_hash

You may use where_values_hash from ActiveRecord::Relation to construct a single SQL query which NOTs your scope's conditions.

Copy
User.where.not(User.admins.where_values_hash)

That's just like saying User.where.not(role: ['admin', 'superuser']), but programmatically.

The resulting SQL is quite pretty.

Copy
SELECT * FROM users WHERE role NOT IN ('admin', 'superuser')

This will not work for scopes using joins or similar, as where_values_hash does not include those. While there is probably a way to do it, I suggest just using a subquery in such cases.

Note that - as always in SQL - this query will not return rows where role is "NULL", since in SQL "NOT NULL = NULL".

makandra has been working exclusively with Ruby on Rails since 2007. Our laser focus on a single technology has made us a leader in this space.

Owner of this card:

Avatar
Arne Hartherz
Last edit:
5 days ago
by Tobias Kraze
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Arne Hartherz to makandra dev
This website uses short-lived cookies to improve usability.
Accept or learn more