Ruby on Rails 7: excluyendo registros con el método #excluding

Podemos encontrarnos con casos en nuestra aplicación de Rails en los que necesitamos excluir algunos conjuntos de registros al recuperar datos. En tales casos, podemos usar el método #excluding para filtrar los registros no deseados.

El método #excluding llegó con Ruby on Rails 7 dentro de ActiveRecord, para excluir registros específicos (o colecciones de registros) de una relación. Además, existe un alias #without.

Veamos cómo hacíamos antes para excluir registros de nuestras consultas y cómo lo podemos hacer ahora con este nuevo método.

Antes, sin #excluding

Nuestra consulta debía usar el where.not para excluir registros (o un conjunto de registros) de los resultados. Por ejemplo, para listar a todos los usuarios excepto al que está logueado en nuestra app usábamos:

User.where.not(id: current_user.id)
# SELECT "users".* FROM "users" WHERE "users"."id" != 1

Ahora, con #excluding

Ruby on Rails 7 incluyó el método #excluding que cumple el mismo propósito que el where.not que usamos arriba. Es importante resaltar que este nuevo método espera un objeto en vez de un atributo. Si miramos el ejemplo anterior, enviábamos el id del objeto.

Entonces, replicando el ejemplo anterior:

User.excluding(current_user)
# SELECT "users".* FROM "users" WHERE "users"."id" != 1

Y si quisiéramos excluir una colección de usuarios, también podemos hacerlo con #excluding , tal como se muestra acá:

User.all.excluding(user_one, user_two)
# SELECT "users".* FROM "users" WHERE "users"."id" NOT IN (1, 2)

Importante
La app nos retornará un ArgumentError si no se especifican registros o si alguno de los registros en la colección (suponiendo que se pase como argumento una colección) no son instancias del mismo modelo que está siendo filtrado. Veamos unos ejemplos:

# Enviando un integer 1 como argumento cuando se espera una instancia de User
User.excluding(1)
ArgumentError (You must only pass a single or collection of User objects to method #excluding.)

# Enviando una instancia de Dog como argumento cuando se espera una instancia de User
dog = Dog.find(1)
User.excluding(dog)
ArgumentError (You must only pass a single or collection of User objects to #excluding.)

Como verás, la incoporación del método #excluding en Ruby on Rails 7 hace que excluir objetos de nuestras consultas a bases de datos sea muy simple y fácil de leer.