Search code examples
ruby-on-railsormrelational-databasehas-many-throughhas-many

usage of "has_many" vs "has_many, through" relation in rails


i'm new to both rails and web-dev.

currently i'm studding "active record Associations" in rails 4

and i got confused on usage of "has_many" vs "has_many, through" relation.

for example, if i have Physician, Appointment, and Patient model in my schema.(As rails guide provides)

and rails tutorial suggests me to do like this.


class Physician < ApplicationRecord
  has_many :appointments
  has_many :patients, through: :appointments
end

class Appointment < ApplicationRecord
  belongs_to :physician
  belongs_to :patient
end

class Patient < ApplicationRecord
  has_many :appointments
  has_many :physicians, through: :appointments
end

but what if i make relation like this


class Physician < ApplicationRecord
  has_many :appointments
end

class Appointment < ApplicationRecord
  belongs_to :physician
  has_many :patients
end

class Patient < ApplicationRecord
  belongs_to :appointment
end

i think both will work fine.

but i want to know whats the differences and why they made "has_many, through" relations.

thank you for reading my question.


Solution

  • has_many through is a way to join two unrelated, independent models/tables and to reduce duplicates/redundancy in tables, where has_many states more directive relation.

    Maybe example with appointments and physicians isn't clear. I'll give a different one.

    class Artist
      has_many :paintings
      has_many :purchases
      has_many :buyers, through: :purchases
    end
    
    class Painting
      belongs_to :artist
    end
    
    class Purchase
      belongs_to :painting
      belongs_to :buyer
    end
    
    class Buyer
       has_many :paintings_buyers
       has_many :painting, through: :purchases
    end
    

    Talking about your example.

    First, there is no convenient way for you to get physician's patents. The only way is:

    physician.appoitments.map(&:patient).uniq
    

    Which will result in

    1. poor performance
    2. inability to filter entities with sql, only with ruby(again poor performance)

    Also, did you notice I used uniq? That's because patient's table will have lots of duplicates when the same patients will be appointed multiple times to the same physician.