通过关联设置has_many
我有 3 个模型
class Battle < ActiveRecord::Base
has_many :battle_videos
has_many :videos, :through => :battle_videos
...
end
class BattleVideo < ActiveRecord::Base
belongs_to :battle
belongs_to :video
validates :code, :presence => true
end
class Video < ActiveRecord::Base
belongs_to :user, :foreign_key => :user_id, :class_name => "User"
...
end
BattleVideo 具有属性“代码”(left_side || right_side),它确定战斗方(战斗中总是有 2 方,因为总是在玩 - 1 对 1,团队对团队等)
我想在模型 video 中指定关联 (left_side_video, right_side_video) ,它获取所选一侧的视频。
现在要获取 SIDE 1(左)的视频 - 使用此代码
Battle.first.battle_videos.where("battle_videos.code = 'left_side'").first.video
我想获取这样的战斗视频
Battle.first.left_side_video
我认为,战斗模型应该如下所示,但它不起作用(仅适用于左侧)
class Battle < ActiveRecord::Base
has_many :battle_videos
has_many :videos, :through => :battle_videos
has_many :left_side_video, :through => :battle_videos, :source => :video, :conditions => ["battle_videos.code = 'left_side'"]
has_many :right_side_video, :through => :battle_videos, :source => :video, :conditions => ["battle_videos.code = 'right_side'"]
end
更新: 有效,但不是以期望的方式。问题出在 includes
链中。 模型 Battle
具有范围 :all_inclusive
,它加载所有关联
scope :all_inclusive, includes(:battle_category).includes(:bets).includes(:votes).includes(:left_side_video).includes(:right_side_video)
生成的 SQL:
Battle Load (0.6ms) SELECT `battles`.* FROM `battles` WHERE (battles.status = 1 AND valid_from < '2012-01-31 13:31:50' AND battles.valid_to > '2012-01-31 13:31:50') ORDER BY battles.id DESC
BattleCategory Load (0.1ms) SELECT `battle_categories`.* FROM `battle_categories` WHERE `battle_categories`.`id` IN (1)
Bet Load (0.1ms) SELECT `bets`.* FROM `bets` WHERE `bets`.`parent_type` = 'Battle' AND `bets`.`parent_id` IN (1, 2)
Vote Load (0.2ms) SELECT `votes`.* FROM `votes` WHERE `votes`.`parent_type` = 'Battle' AND `votes`.`parent_id` IN (1, 2)
BattleVideo Load (0.1ms) SELECT `battle_videos`.* FROM `battle_videos` WHERE `battle_videos`.`battle_id` IN (1, 2) AND (battle_videos.code = 'user_1')
Video Load (0.1ms) SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 3)
注意视频 2 和 4(适用于右侧)- 不加载。我只能访问 1,3 个视频
视频 id, Battle_id (side)
[1 , 1 (lft)]
[2 , 1 (rgt)]
[3 , 2 (lft)]
[4 , 2 (rgt)]
当我删除 .includes(:right_side_video) 时
来自 :all_inclusive
链我得到了这个 sql:
Battle Load (0.6ms) SELECT `battles`.* FROM `battles` WHERE (battles.status = 1 AND valid_from < '2012-01-31 13:39:26' AND battles.valid_to > '2012-01-31 13:39:26') ORDER BY battles.id DESC
BattleCategory Load (0.1ms) SELECT `battle_categories`.* FROM `battle_categories` WHERE `battle_categories`.`id` IN (1)
Bet Load (0.1ms) SELECT `bets`.* FROM `bets` WHERE `bets`.`parent_type` = 'Battle' AND `bets`.`parent_id` IN (1,2)
Vote Load (0.2ms) SELECT `votes`.* FROM `votes` WHERE `votes`.`parent_type` = 'Battle' AND `votes`.`parent_id` IN (1,2)
BattleVideo Load (0.3ms) SELECT `battle_videos`.* FROM `battle_videos` WHERE `battle_videos`.`battle_id` IN (1,2) AND (battle_videos.code = 'left_side')
Video Load (0.1ms) SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 3)
Video Load (0.2ms) SELECT `videos`.* FROM `videos` INNER JOIN `battle_videos` ON `videos`.`id` = `battle_videos`.`video_id` WHERE `battle_videos`.`battle_id` = 1 AND (battle_videos.code = 'right_side') LIMIT 1
Video Load (0.1ms) SELECT `videos`.* FROM `videos` INNER JOIN `battle_videos` ON `videos`.`id` = `battle_videos`.`video_id` WHERE `battle_videos`.`battle_id` = 2 AND (battle_videos.code = 'right_side') LIMIT 1
现在这工作正常。但在 SQL 级别 - 它并不像我想要的那么完美。您可以看到,视频 1、3 以正确的方式加载,
Video Load (0.1ms) SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 3)
但是视频 2、4 通过单独的 sql 加载:
Video Load (0.2ms) SELECT `videos`.* FROM `videos` INNER JOIN `battle_videos` ON `videos`.`id` = `battle_videos`.`video_id` WHERE `battle_videos`.`battle_id` = 1 AND (battle_videos.code = 'right_side') LIMIT 1
Video Load (0.1ms) SELECT `videos`.* FROM `videos` INNER JOIN `battle_videos` ON `videos`.`id` = `battle_videos`.`video_id` WHERE `battle_videos`.`battle_id` = 2 AND (battle_videos.code = 'right_side') LIMIT 1
我想要什么? 由 RUBY 生成的最终 SQL
Video Load (0.1ms) SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 2, 3, 4)
I have 3 models
class Battle < ActiveRecord::Base
has_many :battle_videos
has_many :videos, :through => :battle_videos
...
end
class BattleVideo < ActiveRecord::Base
belongs_to :battle
belongs_to :video
validates :code, :presence => true
end
class Video < ActiveRecord::Base
belongs_to :user, :foreign_key => :user_id, :class_name => "User"
...
end
BattleVideo has attribute "code" (left_side || right_side) which determines BATTLE SIDE (there are always 2 sides in battles, coz always playing -- 1 vs. 1, team vs. team etc.)
I would like to specify association (left_side_video, right_side_video) in model video , which gets video for chosen side.
Now to get video for SIDE 1 (left) - use this code
Battle.first.battle_videos.where("battle_videos.code = 'left_side'").first.video
I want to get my battle videos like this
Battle.first.left_side_video
I think, Battle model should look like this, but it doesnt work (works only left side)
class Battle < ActiveRecord::Base
has_many :battle_videos
has_many :videos, :through => :battle_videos
has_many :left_side_video, :through => :battle_videos, :source => :video, :conditions => ["battle_videos.code = 'left_side'"]
has_many :right_side_video, :through => :battle_videos, :source => :video, :conditions => ["battle_videos.code = 'right_side'"]
end
UPDATE:
Works, but not in desired way. The problem was in includes
chain.
model Battle
has scope :all_inclusive
, which loads all associations
scope :all_inclusive, includes(:battle_category).includes(:bets).includes(:votes).includes(:left_side_video).includes(:right_side_video)
Generated SQL:
Battle Load (0.6ms) SELECT `battles`.* FROM `battles` WHERE (battles.status = 1 AND valid_from < '2012-01-31 13:31:50' AND battles.valid_to > '2012-01-31 13:31:50') ORDER BY battles.id DESC
BattleCategory Load (0.1ms) SELECT `battle_categories`.* FROM `battle_categories` WHERE `battle_categories`.`id` IN (1)
Bet Load (0.1ms) SELECT `bets`.* FROM `bets` WHERE `bets`.`parent_type` = 'Battle' AND `bets`.`parent_id` IN (1, 2)
Vote Load (0.2ms) SELECT `votes`.* FROM `votes` WHERE `votes`.`parent_type` = 'Battle' AND `votes`.`parent_id` IN (1, 2)
BattleVideo Load (0.1ms) SELECT `battle_videos`.* FROM `battle_videos` WHERE `battle_videos`.`battle_id` IN (1, 2) AND (battle_videos.code = 'user_1')
Video Load (0.1ms) SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 3)
NOTICE THAT VIDEOS 2 AND 4 (FOR RIGHT SIDE) - dont load. I can access only 1,3 videos
Videos
id, battle_id (side)
[1 , 1 (lft)]
[2 , 1 (rgt)]
[3 , 2 (lft)]
[4 , 2 (rgt)]
When I remove .includes(:right_side_video)
from :all_inclusive
chain I got this sql:
Battle Load (0.6ms) SELECT `battles`.* FROM `battles` WHERE (battles.status = 1 AND valid_from < '2012-01-31 13:39:26' AND battles.valid_to > '2012-01-31 13:39:26') ORDER BY battles.id DESC
BattleCategory Load (0.1ms) SELECT `battle_categories`.* FROM `battle_categories` WHERE `battle_categories`.`id` IN (1)
Bet Load (0.1ms) SELECT `bets`.* FROM `bets` WHERE `bets`.`parent_type` = 'Battle' AND `bets`.`parent_id` IN (1,2)
Vote Load (0.2ms) SELECT `votes`.* FROM `votes` WHERE `votes`.`parent_type` = 'Battle' AND `votes`.`parent_id` IN (1,2)
BattleVideo Load (0.3ms) SELECT `battle_videos`.* FROM `battle_videos` WHERE `battle_videos`.`battle_id` IN (1,2) AND (battle_videos.code = 'left_side')
Video Load (0.1ms) SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 3)
Video Load (0.2ms) SELECT `videos`.* FROM `videos` INNER JOIN `battle_videos` ON `videos`.`id` = `battle_videos`.`video_id` WHERE `battle_videos`.`battle_id` = 1 AND (battle_videos.code = 'right_side') LIMIT 1
Video Load (0.1ms) SELECT `videos`.* FROM `videos` INNER JOIN `battle_videos` ON `videos`.`id` = `battle_videos`.`video_id` WHERE `battle_videos`.`battle_id` = 2 AND (battle_videos.code = 'right_side') LIMIT 1
Now this works fine. But IN SQL level - its not as perfect as I want. You can see, that videos 1, 3 are loaded in correct way
Video Load (0.1ms) SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 3)
But video 2, 4 load by seperate sqls:
Video Load (0.2ms) SELECT `videos`.* FROM `videos` INNER JOIN `battle_videos` ON `videos`.`id` = `battle_videos`.`video_id` WHERE `battle_videos`.`battle_id` = 1 AND (battle_videos.code = 'right_side') LIMIT 1
Video Load (0.1ms) SELECT `videos`.* FROM `videos` INNER JOIN `battle_videos` ON `videos`.`id` = `battle_videos`.`video_id` WHERE `battle_videos`.`battle_id` = 2 AND (battle_videos.code = 'right_side') LIMIT 1
What I want?
FINAL SQL GENERATED BY RUBY
Video Load (0.1ms) SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 2, 3, 4)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
为什么不尝试使用“连接”而不是“包含”?
您可以像这样创建named_scope
尚未对此进行测试。但完美的查询应该或多或少是相似的。
Why don't you try with "joins" instead of "includes"?
You can create named_scope like this
Haven't tested this yet. But the perfect query should be more or less the similar one.
我认为,您应该使用
preload
而不是includes
来加载关联。preload
不会影响原始查询,它会为您指定的每个关联进行单独的查询。它在 Rails 3.0.x 中运行良好。不知道你用的是哪个版本,可能会有所不同......
I think, you should use
preload
instead ofincludes
for loading associations.preload
doesn't affect original query, it makes separate query for every association you specify.It works great in rails 3.0.x. Dunno which version you use, it may differ...