Stuck in a - at first sight - simple problem in RoR. I am sure that is easy, but none answer here in SO helped me too much.
I have two ActiveRecord models: Foo
has many Bars
:
class Foo < ApplicationRecord
has_many :bars
end
class Bar < ApplicationRecord
belongs_to :foo
end
That works like a charm. But I would like to use another field of Foo
as foreign_key. The default is foo_id
I would like to use custom_id
as my foreign key. So I tried this (as many solutions over the web suggested):
class Foo < ApplicationRecord
has_many :bars, :foreign_key => 'custom_id', :class_name => 'Bars'
end
class Bars < ApplicationRecord
belongs_to :foo, :class_name => 'Foo'
end
But that doesn't work. i.e. ActiveRecord keeps binding Foo
to Bars
using foo_id
.
Note: Including a self.primary_key='custom_id' in Foo would partially work. but I don't think that is a good idea. I want to keep foo_id as the primary key
UPDATE:
Given the feedback -Thank you guys-, I uploaded that example here https://github.com/montenegrodr/temporary_repository_ror :
UPDATE #2:
The answers does not satisfy the question above. Why the test is failing, my assumption is that it shouldn't fail.
UPDATE #3:
There're a couple of new answers I still need to assess. Will do that within 24 hours. Thanks.
UPDATE #4:
Thank you guys for all the answers. But none of them satisfied the criteria. I need to have that test passing. If it is not possible, can someone explain why? Is it a rails constraint?
You need to specify a different primary key for the relationship if you wish to achieve what you are looking to do.
To clarify, this is not the same as changing the primary_key
of the model. This way is only changing the primary key used by the relationship. Please see the bottom of this post for examples.
I changed the keys from both using custom_id
and changed one to foo_id
. This way you have a better idea of what is going on between the models. You can use both custom_id
if you wish, but I would suggest keeping the rails norm of foo_id
for the belongs_to association.
If you want to use both of custom_id you'll have to add some specific foreign_keys
Foo
class Foo < ApplicationRecord
has_many :bars,
primary_key: :custom_id,
foreign_key: :foo_id
end
Bar
class Bar < ApplicationRecord
belongs_to :foo,
primary_key: :custom_id
end
CreateFoos
class CreateFoos < ActiveRecord::Migration[5.2]
def change
create_table :foos do |t|
t.integer :custom_id, index: {unique: true}
t.timestamps
end
end
end
CreateBars
class CreateBars < ActiveRecord::Migration[5.2]
def change
create_table :bars do |t|
t.integer :foo_id, index: true
t.timestamps
end
end
end
Here is the updated Test that should now be passing:
Test
require 'test_helper'
class BarTest < ActiveSupport::TestCase
test "the truth" do
foo = Foo.new(id: 1, custom_id: 100)
bar = Bar.new(foo: foo)
assert bar.foo_id == foo.custom_id
# bar.foo_id = 100
# foo.custom_id = 100
end
end
Examples
Foo.find(1) #<Foo id: 1, custom_id: 100>
Bar.first #<Bar id: 1, foo_id: 100>
Bar.first.foo = #<Foo id: 1, custom_id: 100>
Bar.first.foo == Foo.find(1) # true
As you can see, this method does not change the primary key of Foo
itself. It changes the primary key the relationship between Foo
and Bar
uses. Bar is realated to foo via custom_id: 100
, but foo is still found with it's id: 1
key, not its custom_id
key.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With