One of the things that makes Rails “magical” is that it’s default behaviour is most likely what you would want. One place that this doesn’t happen is when connecting both sides of an association when it isn’t following the normal Rails conventions. An example of this is a self referential association:
class User < ApplicationRecord
has_many :subordinates, class_name: "User", foreign_key: :manager_id
belongs_to :manager, class_name: "User", optional: true
end
manager = User.new
employee = manager.subordinates.build
puts employee.manager # nil ?!?
Well that won’t do. Whats going wrong?
Rails generally does a good job of automatically guessing what the relationships are based on their names.
Normally, if you had a Users class with has_many :posts, rails would:
Post class based on the association name.user_id based on the User class.user the Post class.This works fine in the simple use case, and we can solve problems #1 and #2 by adding the class_name and foreign_key options but that doesn’t resolve #3.
To fix the relationship, we need to explicitly tell Rails which association is on the other side of the subordinates association:
class User < ApplicationRecord
has_many :subordinates, class_name: "User", foreign_key: :manager_id, inverse_of: :manager
belongs_to :manager, class_name: "User", optional: true
end
We can test this by printing out the object IDs and see that the match, meaning this is one single instance:
manager = User.new
employee = manager.subordinates.build
puts manager.object_id # 1234
puts employee.manager.object_id # 1234