Reputation: 578
I've seen a number of answers to questions that address how to use scope blocks in ActiveRecord associations that include passing the object itself into the block like ...
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
The usage of the explicit PK and FK here is due to the legacy relationship between the underlying tables. There are many hundreds of millions of "patrons" in the production system.
As a point of clarification re @TJR - the relationship between Patron and Bar is actual a compound foreign key on the fields :somekey and :baz (or :blah in the reverse direction). ActiveRecord's :foreign_key option doesn't allow arrays.
I've discovered that unfortunately this prevents subsequent has_many :throughs from working as expected.
class Patron < ActiveRecord::Base
has_many :bars,
->(patron) { baz: patron.blah },
foreign_key: :somekey,
primary_key: :somekey
has_many :drinks, through: :bars
end
Using the through association produces errors like ...
ArgumentError: wrong number of arguments (0 for 1)
The association between bar and drinks is a classic has_many with the standard foreign and primary key (bar_id, id).
I have come up with some ugly work arounds that allow me to accomplish the desired functionality but the whole thing smells terrible. So far the best of these look like
class Patron < ActiveRecord::Base
has_many :bars,
->(patron) { baz: patron.blah },
foreign_key: :somekey,
primary_key: :somekey
def drinks
bars.drinks
end
end
I've received the existing Bar class and it consists of many hundreds of millions of records, as I previously mentioned, making a database side change difficult.
Some posts seemed to suggest a dynamic string evaluation inside the proc to avoid having to pass in the current patron object - but as mentioned in other posts, this doesn't work.
Please advise me on what I might do to get the has_many through relationship working.
Upvotes: 5
Views: 616
Reputation: 2860
I just have tried this kind of associations in Rails 4.2. It works pretty well:
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
Check associations:
> 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">]>
Upvotes: 0