通过关联设置has_many

发布于 2024-12-29 18:09:56 字数 4745 浏览 0 评论 0原文

我有 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

溺渁∝ 2025-01-05 18:09:56

为什么不尝试使用“连接”而不是“包含”?

您可以像这样创建named_scope

    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'"
    }

尚未对此进行测试。但完美的查询应该或多或少是相似的。

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.

初心未许 2025-01-05 18:09:56

我认为,您应该使用 preload 而不是 includes 来加载关联。

范围:all_inclusive、预加载(:battle_category、:bets、:votes、:left_side_video、:right_side_video)

preload 不会影响原始查询,它会为您指定的每个关联进行单独的查询。

它在 Rails 3.0.x 中运行良好。不知道你用的是哪个版本,可能会有所不同......

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...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文