我在Rails应用中有两个模型 - Tournament
和Player
通过连接表关联:
class Tournament < ApplicationRecord
has_many :tournament_players
has_many :players, through: :tournament_players
end
class Player < ApplicationRecord
has_many :tournament_players
has_many :tournaments, through: :tournament_players
scope :selected, -> (tournament) { includes(:tournaments).where(tournaments: {id: tournament.id}) }
end
我有很多锦标赛,每个锦标赛都有很多玩家。玩家可以参加很多锦标赛。范围
scope :selected, -> (tournament) { includes(:tournaments).where(tournaments: {id: tournament.id}) }
成功找到已经加入锦标赛的所有球员,因为锦标赛是一个参数。
我喜欢的是一个相反的范围 - 返回尚未添加到特定锦标赛的所有玩家。我试过了
scope :not_selected, -> (tournament) { includes(:tournaments).where.not(tournaments: {id: tournament.id}) }
但这会让许多相同的球员回归,我认为因为球员是其他锦标赛的一部分。 SQL的内容类似于:
SELECT "players".*, "tournaments”.* FROM "players" LEFT OUTER JOIN
"tournament_players" ON "tournament_players"."player_id" =
"players"."id" LEFT OUTER JOIN "tournaments" ON "tournaments"."id" =
"tournament_players"."tournament_id" WHERE ("tournaments"."id" != $1)
ORDER BY "players"."name" ASC [["id", 22]]
我还尝试了this question上的建议 - 使用
scope :not_selected, -> (tournament) { includes(:tournaments).where(tournaments: {id: nil}) }
但这似乎不起作用 - 它只返回一个空数组,我认为,因为玩家作为单独比赛的一部分存在于联接表中。 SQL的内容类似于:
SELECT "players”.*, "tournaments”.* FROM "players" LEFT OUTER JOIN
"tournament_players" ON "tournament_players"."player_id" =
"players"."id" LEFT OUTER JOIN "tournaments" ON "tournaments"."id" =
"tournament_players"."tournament_id" WHERE "tournaments"."id" IS NULL
ORDER BY "players"."name" ASC
答案 0 :(得分:4)
您需要做的是:
此代码应该这样做:
# player.rb
scope :not_selected, -> (tournament) do
joins("LEFT JOIN tournament_players tp ON players.id = tp.player_id AND tp.tournament_id = #{tournament.id}").where(tp: {tournament_id: nil})
end
如果只有Rails有更好的方法来编写具有附加条件的LEFT JOIN查询......
一些注意事项:
eager_load
。除了我最了解它不支持自定义条件外,它还会为所有相关对象创建模型,而这些模型是您不需要的。答案 1 :(得分:0)
好吧试试这个:
includes(:tournaments).distinct.where.not(tournaments: {id: tournament.id})