双向自引用关联

时间:2016-02-26 01:20:51

标签: ruby-on-rails ruby activerecord

我有一个包含2个模型的简单数据库:

class Fighter < ActiveRecrod::Base
   has_many :fights
   has_many :opponents, through :fights
end

class Fight < ActiveRecord::Base
   belongs_to :fighter
   belongs_to :opponent, class_name: 'Fighter'
 end`

我应该为此添加什么:

a = Fighter.create
b = Fighter.create
a.opponents << b
b.fights <- should return a value`

a.fights应该返回新创建的战斗

b.fights也应该返回新创建的战斗

到目前为止只有a.fights返回值

我提出了这样的想法:

class Fight < ActiveRecord::Base
  belongs_to :fighter
  belongs_to :opponent, class_name: 'Fighter'
  after_create :create_association

  def create_association
    Fighter.find(opponent_id).fights << self
  end
end

但是从Fight调用另一个模型似乎不对。

我还试图在has_man :fights中重载Fighter,以便它需要lambda

has_many :fights, -> { where("fighter_id = ? OR opponent_id = ?", self.id, self.id)  但我经常得到错误 - 错误的参数数量(0表示2)

研究了一下后,我知道我应该使用双向自引用关联,类似于:Bidirectional self referential associations

但是当我遵循这个时,我会在表Fight中收到两个战斗记录。一个与一个战斗机相关联,另一个与第二个战斗机相关联。

我很感激如何解决这个问题的帮助或一些提示

1 个答案:

答案 0 :(得分:2)

如果您不想定义反向关系(如this Railscast中所述),我不认为您每次都可以避免创建两个战斗。你使用SELECT ALL fr.sku, fr.dispo, eutemp.dispo FROM totfrhrl as fr LEFT JOIN totrihrl_temp eutemp ON fr.sku = eutemp.sku WHERE exists(select 1 from totfrhrl_temp frtemp where frtemp.sku = fr.sku and ( fr.dispo != frtemp.dispo OR fr.command != frtemp.command OR fr.eta != frtemp.eta)) Union ALL SELECT ALL eu.sku, eu.dispo, frtemp.dispo FROM totrihrl as eu LEFT JOIN totfrhrl_temp frtemp ON eu.sku = frtemp.sku WHERE exists(select 1 from totrihrl_temp eutemp where eutemp.sku = eu.sku and (eu.dispo != eutemp.dispo OR eu.command != eutemp.command OR eu.eta != eutemp.eta)) and not exists (select 1 from totfrhrl as fr where fr.sku=eu.sku) 的想法是有效的,只需要添加对手而不是战斗本身,就像这样:

after_create

反向关系在性能和存储方面会更好,但不能无缝使用:

class Fight < ActiveRecord::Base
  belongs_to :fighter
  belongs_to :opponent, class_name: 'Fighter'

  after_create :create_association

  def create_association
    opponent = Fighter.find(opponent_id)
    unless opponent.opponents.include?(fighter)
      Fighter.find(opponent_id).opponents << fighter
    end
  end
end

从这里开始,如果你想让所有对手都成为战士,你应该做class Fighter < ActiveRecord::Base has_many :fights has_many :opponents, through: :fights has_many :inverse_fights, :class_name => "Fight", :foreign_key => "opponent_id" has_many :inverse_opponents, :through => :inverse_fights, :source => :fighter end