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)
Why don't you try with "joins" instead of "includes"?
You can create named_scope like this
named_scope : left_side_video, {
:select => "videos.*",
:joins => "INNER JOIN battle_videos ON battle_videos.video_id = videos.id INNER JOIN battles on battles.id = battle_videos.battle_id",
:conditions=>"battle_videos.code = 'left_side'"
}
Haven't tested this yet. But the perfect query should be more or less the similar one.
I think, you should use preload
instead of includes
for loading associations.
scope :all_inclusive, preload(:battle_category, :bets, :votes, :left_side_video, :right_side_video)
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...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.