Anujan
Anujan

Reputation: 938

Rails dynamic foreign key for has_many relationship

I have a User model that's used to hold all Users. There's two types on users in my apps, mentors and students, but they have the same structure.

I want to do something like this.

has_many :requests, :foreign_key => mentor? ? 'mentor_id' : 'student_id', :class_name => 'Request'

I have the mentor? method in the model but it gives me a method_missing error. I'm assuming it's because it's looking for the method in dynamic_matchers.rb

This is the error it gives

/var/lib/gems/1.9.1/gems/activerecord-3.2.13/lib/active_record/dynamic_matchers.rb:55:in 'method_missing': undefined method 'mentor?' for #<Class:0x00000001b40630> (NoMethodError)

Is there a way to go about this without making a seperate models for the Student and Mentor? I feel that'd it be unnecessary seeing that they both use the same fields.

Upvotes: 2

Views: 1845

Answers (2)

Raindal
Raindal

Reputation: 3237

Ok so let me correct that: this is not possible, and even if in a utopian world it was or you found a workaround for this, it is certainly not correct.

What you are in fact looking for is a mix between inheritance (STI or Single Table Inheritence in Rails) and polymorphism.

How can you implement this the right way?

You have a user model, Students and Mentors ar both Users so they will both inherit for this model.

class User < ActiveRecord::Base
end

class Student < User
end

class Mentor < User
end

What does it implies? Well it implies that the User model holds (without you doing anything else) a field type wich will either contain 'Student' or 'Mentor' or nothing depending on how you initialize your object:

user = User.create()
student = Student.create()
mentor = Mentor.create()

Now, your Request can either belong to a Student or to a Mentor. So here you could set a polymorphic association like so:

class Student < User
  has_many :requests, as: :owner
end

class Mentor < User
  has_many :requests, as: :owner
end

class Request < ActiveRecord::Base
  belongs_to :owner, polymorphic: true
end

Now you can write:

student.requests
mentor.requests
request.owner

Upvotes: 5

Bruno Sapienza
Bruno Sapienza

Reputation: 143

I'm not quite sure about what do you need. Same model structure, and one user can be a mentor for many students? and one student can have one mentor? is that?

if it is, what about a self join?

class User < ActiveRecord::Base
  has_many :students, :class_name => "User", :foreign_key => "mentor_id"
  belongs_to :mentor, :class_name => "User"
end

this way you will be able to call student.mentor and mentor.students

Upvotes: 1

Related Questions