在 Rails 中,如何在单个查找器中查询两个 has_many 关联?

In Rails, how do I query two has_many associations in a single finder?

我正在使用 Rails 4.2。我有以下用户模型和几个 has_many 关联

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

我想查找没有任何地址的特定角色的所有用户。我认为下面会做到这一点

> users = User.includes(:roles, :addresses).where(:roles => {:name => 'User'}, :addresses => {:user_id => nil})

但是当我检查结果时,我仍然得到有地址的结果……

2.7.1 :012 > users.last.addresses.count
…
 => 2

编写查询这两个 has_many 关联的查找器的正确方法是什么?

检查父 ID 为 nil 的子记录与 Rails 4 中的方法类似。但是如果这不起作用,您可以使用 NOT IN 子句组合:

User
  .where
  .not(
    id: User
          .joins(:addresses, :roles)
          .where(roles: { name: 'User' })
          .select(:id)
  )

它基本上是通过用户 ID 过滤掉所有具有关联地址、具有角色且角色名称正好是“用户”的用户行。您最终会得到这样的 SQL 查询:

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'
)