Question

I have encountered some unexpected behavior in Active Record (3.2.13):

There is a simple scope on my model:

class User < ActiveRecord::Base
  scope :verified, lambda { where('verified = 1') }
end

This can be used fine on its own:

  User.verified.to_sql
  #=> "SELECT \"users\".* FROM \"users\"  WHERE (verified = 1)"

When I concatenate where-clauses, they are anded as expected:

  User.where(company_id: 1).where(company_id: 2).to_sql
  "SELECT \"users\".* FROM \"users\"  WHERE \"users\".\"company_id\" = 1 AND \"users\".\"company_id\" = 2"

Problem:

However, when I chain a scope, my first where-clause is nuked, the last one wins in a merge:

User.where(company_id: 1).where(company_id: 2).verified.to_sql
"SELECT \"users\".* FROM \"users\"  WHERE \"users\".\"company_id\" = 2 AND (verified = 1)"

How to apply a scope on a relation with existing conditions?

(Those existing conditions have been established through cancan's load_and_authorize_resource so I cannot just apply those where clauses after applying my scope.)

Was it helpful?

Solution

This gist sums up our findings on the issue. https://gist.github.com/kirel/5678865

It boils down to ActiveRecord::Scoping::Named::ClassMethods#scope using ActiveRecord::SpawnMethods#merge which implements the unexpected but intended behavior.

Rails 4 will not use merge und thus behave as expected (see https://github.com/rails/rails/commit/cd26b6ae7c1546ef8f38302661bbedf8cb487311). Meanwhile the workaround is to just avoid scope and use class methods instead.

OTHER TIPS

Change it to

User.verified.where(company_id: 1).where(company_id: 2).to_sql

It seems that when you used scope after the where clause it simply create a new hash of the chained where clause and then AND it at the end.

hence

User.where(company_id: 1).where(company_id: 2).verified.to_sql

Gives you

"SELECT \"users\".* FROM \"users\"  WHERE \"users\".\"company_id\" = 2 AND 
(verified = 1)"

BUT

User.where(company_id: 1).where(contact_id: 2).verified.to_sql

Gives you

"SELECT \"users\".* FROM \"users\"  WHERE \"users\".\"company_id\" = 2 AND
\"users\".\"contact_id\" = 2 AND (verified = 1)"
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top