Qaisar Nadeem
Qaisar Nadeem

Reputation: 2434

has_one/has_many rails association with alternate source ID other then primary key

I have 3 models.

Model A belongs to model B and model B has many model C

by using through I can set in model A class has_many :c,through: :b

Now whenever I call a_instance.cs It would JOIN with table B unnecessarily What I want is direct association of A & C using b_id in both A & C class.

So how can I write a has_one/has_many rails association without through clause when both Source and Destination entities has same foreign_key of 3rd entity? (b_id for this example)

class C
  belongs_to :b #b_id column is there in DB
end

class B
  has_many :cs
end

class A
  belongs_to :b #b_id column is there in DB
  has_many :cs , through: :b #This is the association in Question

  #WHAT I WANT TO DO. So something in place of primary_key which would fetch C class directly.
  has_many :cs ,class_name: 'C',foreign_key: 'b_id',primary_key: 'b_id'
end

Upvotes: 4

Views: 1241

Answers (2)

JBlanco
JBlanco

Reputation: 526

Following your example of classes A,B & C and changing only the implementation of class A.

class A
  belongs_to :b

  def cs
    self.id = b_id
    b_cast = self.becomes(B)
    b_cast.cs
  end
end

Console

A.first                    ==>#<A id: 105, b_id: 1 ...>
B.first                    ==>#<B id: 1 ...>
C.all                      ==>#<C id: 1, b_id: 1 ...>, 
                              #<C id: 2, b_id: 1 ...>, 
                              #<C id: 3, b_id: 1 ...>, 
                              #<C id: 4, b_id: 1 ...>



A.first.cs
A Load (0.2ms)  SELECT  "as".* FROM "as" ORDER BY "as"."id" ASC LIMIT ?  [["LIMIT", 1]]
C Load (0.1ms)  SELECT  "cs".* FROM "cs" WHERE "cs"."b_id" = ? LIMIT ?  [["b_id", 1], ["LIMIT", 11]]                 

A.first.cs                 ==>#<C id: 1, b_id: 1 ...>, 
                              #<C id: 2, b_id: 1 ...>, 
                              #<C id: 3, b_id: 1 ...>, 
                              #<C id: 4, b_id: 1 ...>

Official #becomes(klass) documentation can be found here!

On other end, the same effect can be achieved creating an instance of the class B and giving it the id stored in A.first.b_id as follows:

B.new(id: A.first.b_id).cs
A Load (0.2ms)  SELECT  "as".* FROM "as" ORDER BY "as"."id" ASC LIMIT ?  [["LIMIT", 1]]
C Load (0.1ms)  SELECT  "cs".* FROM "cs" WHERE "cs"."b_id" = ? LIMIT ?  [["b_id", 1], ["LIMIT", 11]]                 

B.new(id: A.first.b_id).cs ==>#<C id: 1, b_id: 1 ...>, 
                              #<C id: 2, b_id: 1 ...>, 
                              #<C id: 3, b_id: 1 ...>, 
                              #<C id: 4, b_id: 1 ...>

Upvotes: 0

Cong Chen
Cong Chen

Reputation: 2436

An example, Teacher and Student models belong to Group, Teacher has many Student:

class Group < ApplicationRecord
  has_many :teachers
  has_many :students
end

class Teacher < ApplicationRecord
  belongs_to :group
  has_many :students, :foreign_key => 'group_id', :primary_key => 'group_id'
end

class Student < ApplicationRecord
  belongs_to :group
end

And you can retrieve a teacher's students in a group like this:

teacher = Teacher.find(:teacher_id)
students = teacher.students

Upvotes: 1

Related Questions