ruby-on-railshas-many-through

Is has_many still necessary when has_many through exists?


I have what I feel like is a super simple question, but I can't find an answer anywhere!

Question:

If I previously had a has_many relationship like this: has_many :wikis, do I keep this relationship if later on I create a has_many through relationship like the following?

has_many :collaborators
has_many :wikis, through: :collaborators

This is all in my User model.

Background:

In my rails app, I have a User model and a Wiki model. I just gave users the ability to collaborate on private wikis so I migrated a Collaborator model and then came the step to create the has_many through relationships. I wasn't sure if I still needed has_many :wikis after putting has_many :wikis, through: :collaborators.

The reason I am confused is because Users should still be able to create wikis without collaborators and I'm not sure how the has_many through relationship works under the hood.

Originally I had only User and Wiki with a one-to-many relationship.

# User model
class User < ApplicationRecord
  ...    
  has_many :wikis # should I delete this?
  has_many :collaborators
  has_many :wikis, through: :collaborators
  ...
end

# Collaborator model
class Collaborator < ApplicationRecord
  belongs_to :user
  belongs_to :wiki
end

# Wiki model
class Wiki < ApplicationRecord
  belongs_to :user

  has_many :collaborators, dependent: :destroy
  has_many :users, through: :collaborators
  ...
end

Solution

  • Is has_many still necessary when has_many through exists?

    has_many not necessary when presence has_many through like your model

    has_many :wikis # should I delete this?
    has_many :collaborators
    has_many :wikis, through: :collaborators
    

    should I delete this?

    Yes, you can delete this one, you don't need this as the same belongs_to

    From The has_many Association

    A has_many association indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a belongs_to association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing authors and books, the author model could be declared like this:

    enter image description here

    From The has_many :through Association:

    A has_many :through association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look 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
    

    enter image description here

    You can work with only has_many association without has_many :through, but this is one-to-many, this not many-to-many

    Update

    Look, one physician may have many patients, on the other hand, one patient may have many physicians if you use has_many association without through for patient then this called one-to-many association, that means one physician has many patients, on the other hand, one patient belongs to one physician, and now association looks like this

    class Physician < ApplicationRecord
      has_many :patients
    end
     
    class Patient < ApplicationRecord
      belongs_to :physician
    end
    

    Update 2

    The has_many through the standard format your models after edited

    # User model
    class User < ApplicationRecord
      ...    
      has_many :collaborators
      has_many :wikis, through: :collaborators
      ...
    end
    
    # Collaborator model
    class Collaborator < ApplicationRecord
      belongs_to :user
      belongs_to :wiki
    end
    
    # Wiki model
    class Wiki < ApplicationRecord
      has_many :collaborators, dependent: :destroy
      has_many :users, through: :collaborators
      ...
    end