问题
Here is my problem (a bit simplified) :
I've got the following models :
class User
has_many :group_users
has_many :groups, through: :group_users
class Group
has_many :group_users
has_many :users, through: :group_users
class GroupUser
belongs_to :group
belongs_to :user
scope :belonging_to_group ->(group) {where(group_id = group.id)}
I would like to scope users that are not in one specific group, let's say veggies for examples, something that would begin like this :
scope :not_in_group, ->(group)
I've tried stuffs with having clauses like that:
scope :not_in_group, ->(group) {joins(:group_users).merge(::GroupUser.belonging_to_group(group)).group(:title).having('count(group_users.user_id) = 0')
but nothing seems to work
EDIT : I've got another problem now, you may want to check this if you're calling your scope from an other class' class method : Rails - Use a class method from a another class method with one attribute of the second class
回答1:
Try This
scope :not_in_group, -> group_id {joins(:group_users).where('group_users.group_id != ?', group_id)}
For left join, try this:
scope :not_in_group, -> group_id {joins("left join group_users on users.id = group_users.user_id").where('group_users.group_id != ?', group_id)}
回答2:
According to the rails documentation:
Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible on the association objects. source.
class User
#...
# Retrieve all users not in a specific group
# example: User.not_in_group(Group.find(5))
def self.not_in_group(group)
includes(:group_users).where("group_users.group_id != ?", group.id)
end
end
If you are determined to use a scope, here it is:
scope :not_in_group, ->(group) {includes(:group_users).where("group_users.group_id != ?", group.id)}
回答3:
EDIT : I realised that it was not working fine if a user had 2 groups, as he would appear as not in both. I believe that MySQL found an other group_users with the user's id and the other group_id.
So I changed to this, which seems to work :
scope :not_in_group, ->(group){
in_group = User.joins(:group_users).where("group_users.group_id = ?", group.id)
where(arel_table[:id].not_in in_group.map(&:id))
}
In two times but working.
Previous solution : only works if your user has one group.
Finally I found a solution, I had to pass raw SQL in joins()
and where()
as @Himesh suggested me to do. Thanks a lot to you and @lightswitch05 for your help.
Both of your propositions did not select the users without any group, here is a solution that works :
def not_in_group(group)
joins('LEFT JOIN group_users ON group_users.user_id = users.id').where("group_users.group_id != ? OR group_users.group_id is null", group.id)
end
Found some held there : Rails 3 ActiveRecord where clause where id is set or null
来源:https://stackoverflow.com/questions/24186472/rails-3-scope-find-users-not-in-a-specific-group