I’m using Rails 4.2. I have the following user model with a couple of has_many associations
class User < ActiveRecord::Base
…
has_many :roles, through: :roles_users
has_many :addresses, dependent: :destroy, as: :addressable, inverse_of: :addressable
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :roles_users
class RolesUser < ActiveRecord::Base
belongs_to :user
belongs_to :role
end
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
alias :user :addressable
I would like to find all users of a specific role without any addresses. I thought the below would do it
> users = User.includes(:roles, :addresses).where(:roles => {:name => 'User'}, :addresses => {:user_id => nil})
But when I check the results, I’m still getting results that have addresses …
2.7.1 :012 > users.last.addresses.count
…
=> 2
What’s the proper way to write a finder that queries these two has_many associations?
CodePudding user response:
Checking for children records with a nil parent id is like the way of doing this in Rails 4. But if that doesn't work, you could use the NOT IN clause combination:
User
.where
.not(
id: User
.joins(:addresses, :roles)
.where(roles: { name: 'User' })
.select(:id)
)
It's basically filtering out by the user id all those user rows that have an address associated, have a role, and the role name is exactly "User". You end up with a SQL query like this:
SELECT "users".*
FROM "users"
WHERE "users"."id" NOT IN (
SELECT "users"."id"
FROM "users"
INNER JOIN "roles_users" ON "roles_users"."user_id" = "users"."id"
INNER JOIN "roles" ON "roles"."id" = "roles_users"."role_id"
WHERE "roles"."name" = 'User'
)