ActiveRecord查找关联详细信息

时间:2009-09-15 14:49:49

标签: ruby-on-rails activerecord

假设我在做用户和游戏的配对。 我有包含用户和游戏的模型。

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

3 个答案:

答案 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]])

如果有效,请告诉我: - )