Reputation: 3080
In rails I have 3 models: Movie
, Vote
and User
class Movie
has_many :votes
end
class User
has_many :votes
end
class Vote
belongs_to :movie
belongs_to :user
end
I have a scope voted_by
for model Movie
, which returns all movies voted by given user.
class Movie
scope :voted_by, ->(user) {
includes(:votes).where(votes: { user: user })
}
end
Now I want another scope not_voted_by
which does exact the opposite: it should returns all movies that are NOT voted by given user.
How can I achieve that?
What I come up with so far is:
scope :not_voted_by, ->(user) {
includes(:votes).where.not(votes: { user: user }).or(
includes(:votes).where(votes: { user: nil })
)
}
It works when a movie is voted by one or zero user. But it doesn't work when the movie is voted by multiple users.
For example if movie M
is voted by both user A
and B
. Movie.not_voted_by(A)
still includes M
while it shouldn't.
Thanks in advance!
Upvotes: 1
Views: 32
Reputation: 679
Includes in SQL means left join. So you add all records in the left where user not a user
. SQL
SELECT *
FROM movies
LEFT JOIN votes ON (votes.movie_id = movies.id)
WHERE votes.user_id <> 1
If you have e.g. in table.
id | movie_id | user_id
-----------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
Then you query exclude only first record if user.id = 1
.
So, I think you need to exclude voted movies by negate previous scope
scope :not_voted_by, ->(user) { where.not(id: voted_by(user) }
Upvotes: 3