ActiveRecord has_many通过与arg的作用域关联

时间:2016-03-08 16:58:46

标签: ruby-on-rails ruby activerecord

我已经看到了一些问题的答案,这些问题解决了如何在ActiveRecord关联中使用范围块,包括将对象本身传递到块中,如... ...

class Patron < ActiveRecord::Base
  has_many :bars, 
           ->(patron) { baz: patron.blah },
           foreign_key: :somekey,
           primary_key: :somekey
end

class Bar < ActiveRecord::Base
  belongs_to :patron,
             ->(bar) { blah: bar.baz },
             foreign_key: :somekey,
             primary_key: :somekey
end

这里使用显式PK和FK是由于底层表之间的遗留关系。生产系统中有数以亿计的“顾客”。

作为澄清点@TJR - Patron和Bar之间的关系实际上是字段上的复合外键:somekey和:baz(或者反向的blah)。 ActiveRecord:foreign_key选项不允许使用数组。

我发现不幸的是,这会阻止后续的has_many:tos按预期工作。

class Patron < ActiveRecord::Base
  has_many :bars,
           ->(patron) { baz: patron.blah },
           foreign_key: :somekey,
           primary_key: :somekey
  has_many :drinks, through: :bars
end

使用直通关联会产生错误,如...

  

引发ArgumentError:          错误的参数数量(0表示1)

bar和drink之间的关联是经典的has_many,带有标准的外键和主键(bar_id,id)。

我想出了一些丑陋的工作,让我能够完成所需的功能,但整个事情闻起来很糟糕。到目前为止,其中最好的看起来像

class Patron < ActiveRecord::Base
  has_many :bars, 
           ->(patron) { baz: patron.blah },
           foreign_key: :somekey,
           primary_key: :somekey
  def drinks
    bars.drinks
  end
end

我已收到现有的Bar类,它包含数亿条记录,正如我之前提到的那样,使数据库方面的改变变得困难。

有些帖子似乎建议在proc中进行动态字符串评估,以避免传递当前的顾客对象 - 但正如其他帖子所述,这不起作用。

请告诉我如何让has_many通过关系工作。

1 个答案:

答案 0 :(得分:0)

我刚刚在Rails 4.2中尝试过这种关联。它运作得很好:

class Patron < ActiveRecord::Base
  has_many :bars,
           ->(patron) { where(baz: patron.blah) },
           foreign_key: :patron_id,
           primary_key: :id

  has_many :drinks, through: :bars
end

class Bar < ActiveRecord::Base
  belongs_to :patron,
             ->(bar) { where(blah: bar.baz) },
             foreign_key: :patron_id,
             primary_key: :id

  has_many :drinks
end

class Drink < ActiveRecord::Base
end

检查关联:

> p1 = Patron.first
> p1.drinks
  Drink Load (0.8ms)  SELECT "drinks".* FROM "drinks" INNER JOIN "bars" ON "drinks"."bar_id" = "bars"."id" WHERE "bars"."patron_id" = 1 AND "bars"."baz" = 1  [["patron_id", 1], ["baz", 1]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Drink id: 3, name: "drink 3", bar_id: 2, created_at: "2017-04-07 03:30:06", updated_at: "2017-04-07 03:30:06">]>