假设我在做用户和游戏的配对。 我有包含用户和游戏的模型。
class Game < ActiveRecord::Base
has_and_belongs_to_many :users
class User < ActiveRecord::Base
has_and_belongs_to_many :games
游戏可以有很多用户,用户可以玩很多游戏。由于HASBM,我有一个名为games_users的表。
我想搜索并找到等待玩家的游戏,这些玩家也不包含玩家的用户名(即我不想将同一玩家添加到游戏中两次......)
我想要这样的事情:
@game = Game.find_by_status(Status :: WAITING_USERS,:condition =&gt;“game.users.doesnt_contain('username = player')
但我不确定该怎么做?
更新:
使用jdl的解决方案,我获得了运行的代码,但是获取了我试图在结果中返回的项目。这是我的测试代码:
logger.debug "Excluding user: #{@user.id}"
games = Game.excluding_user(@user)
if (games != nil && games.count > 0)
@game = Game.find(games[0].id)
games[0].users.each {
|u|
logger.debug "returned game user: #{u.id}"
}
end
(上面的代码也引出了两个问题.... - 我如何得到一个游戏而不是一个数组的结果,以及如何获得它的非只读版本;这就是为什么我做第二个Game.find ...)
这是日志中的输出:
Excluding user: 2
Game Load (0.3ms) SELECT `games`.* FROM `games` left outer join games_users gu on gu.game_id = games.id WHERE (gu.game_id is null or gu.user_id != 2)
Game Columns (1.0ms) SHOW FIELDS FROM `games`
SQL (0.2ms) SELECT count(*) AS count_all FROM `games` left outer join games_users gu on gu.game_id = games.id WHERE (gu.game_id is null or gu.user_id != 2)
Game Load (0.1ms) SELECT * FROM `games` WHERE (`games`.`id` = 3)
games_users Columns (6.8ms) SHOW FIELDS FROM `games_users`
User Load (0.9ms) SELECT * FROM `users` INNER JOIN `games_users` ON `users`.id = `games_users`.user_id WHERE (`games_users`.game_id = 3 )
returned game user: 1
returned game user: 2
答案 0 :(得分:2)
通过两个步骤可能会更容易。
第1步获取用户参与的游戏列表:
games_playing = user.games.for_status('playing')
第2步获取播放器的开放游戏列表:
open_games = Game.for_status('waiting').not_including(games_playing)
Game
类中有一个额外的命名范围:
named_scope :not_including, lambda {|g| { :conditions => ["id not in (?) ", g] }}
答案 1 :(得分:1)
命名范围是您的朋友。
例如:
class Game < ActiveRecord::Base
has_and_belongs_to_many :users
named_scope :for_status, lambda {|s| {:conditions => {:status => s}}}
named_scope :excluding_user, lambda {|u| {:conditions => ["gu.game_id is null or gu.game_id not in (select game_id from games_users where user_id = ?) ", u.id], :joins => "left outer join games_users gu on gu.game_id = games.id", :group => "games.id" }}
end
这将允许您执行以下操作:
user = User.first # Or whoever.
games_in_progress = Game.for_status("playing")
games_in_progress_for_others = Game.excluding_user(user).for_status("playing")
# etc...
此外,既然你说你是Rails的新手,你可能没有意识到这些命名的范围在你遍历关联时也会起作用。例如:
user = User.first
users_games_in_waiting = user.games.for_status("waiting")
答案 2 :(得分:0)
您可以按照自己的方式执行查询,并使用切片方法从结果中删除用户(但如果用户已经添加到很多游戏中,这不是很好的性能)
有点像这样
a = [ "a", "b", "c" ]
a.slice!(1) #=> "b"
a #=> ["a", "c"]
或编写自定义sql查询(使用find_by_sql并使用!= user_id将其从查询中排除。
我不确定是否有一种“纯粹的”Rails方法可以在查找中使用它而不使用自定义查询。
修改强>
你可以做这样的事情,漂亮的“Railsy”,
@game = Game.find_by_status(Status::WAITING_USERS, :conditions => ["id NOT IN #{user_id}"])
或者对于多个用户,一个数组
@game = Game.find_by_status(Status::WAITING_USERS, :conditions => ["id NOT IN (?)", [1,2,3]])
如果有效,请告诉我: - )