Настройка has_many через ассоциацию - PullRequest
1 голос
/ 31 января 2012

У меня есть 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, команда против команды и т. д.)

Я бы хотел указать ассоциацию (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 видео

Идентификатор видео, идентификатор боя (сторона)

[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 загружается отдельными 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

Что я хочу? FINAL SQL, созданный RUBY

Video Load (0.1ms)  SELECT `videos`.* FROM `videos` WHERE `videos`.`id` IN (1, 2, 3, 4)

Ответы [ 2 ]

0 голосов
/ 31 января 2012

Я думаю, вы должны использовать preload вместо includes для загрузки ассоциаций.

область действия: all_inclusive, предварительная загрузка (: категория_ сражения,: ставки,: голоса,: left_side_video,: right_side_video)

preload не влияет на исходный запрос, он создает отдельный запрос для каждой указанной вами ассоциации.

Отлично работает в rails 3.0.x. Не знаю, какую версию вы используете, она может отличаться ...

0 голосов
/ 31 января 2012

Почему бы вам не попробовать "соединения" вместо "включает"?

Вы можете создать 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'"
    }

Еще не проверял это. Но идеальный запрос должен быть более или менее похожим.

...