Search code examples
ruby-on-railsrubymodelmodel-associations

Multiple association types between 2 models


The following are my three models: many users can each have many products (and vice versa) through an associations model.

class Product < ActiveRecord::Base
  has_many :associations
  has_many :users, :through => :associations
end

class User < ActiveRecord::Base
  has_many :associations
  has_many :products, :through => :associations
end

class Association < ActiveRecord::Base
  belongs_to :user
  belongs_to :product
end

This works great. But now I want to add 2 different association types, a 'strong-association' and 'weak-association' on top of the original association.

What is the best way to go about this?

So far I've considered 1) adding a type column to my association table to specify whether it is a strong/medium/weak association 2) adding a strong-association and also a weak-association record. Both methods seem troublesome when I want to display only a particular association type in my rails view.

I also want to be able to easily change the association type in my project.


Solution

  • You should definitely add a new field to your associations join table: this is the correct way to store this relationship. There's various things you could do after that.

    You could add some new has_many associations:

    class Product < ActiveRecord::Base
      has_many :associations
      has_many :users, :through => :associations
      has_many :weak_associated_users, :class_name => "User", :through => :associations, :source => :user, :conditions => ["associations.strength = ?", "weak"]
      has_many :medium_associated_users, :class_name => "User", :through => :associations, :source => :user, :conditions => ["associations.strength = ?", "medium"]
      has_many :strong_associated_users, :class_name => "User", :through => :associations, :source => :user, :conditions => ["associations.strength = ?", "strong"]
    end
    
    class User < ActiveRecord::Base
      has_many :associations
      has_many :products, :through => :associations
      has_many :weak_associated_products, :class_name => "Product", :through => :associations, :source => :product, :conditions => ["associations.strength = ?", "weak"]
      has_many :medium_associated_products, :class_name => "Product", :through => :associations, :source => :product, :conditions => ["associations.strength = ?", "medium"]
      has_many :strong_associated_products, :class_name => "Product", :through => :associations, :source => :product, :conditions => ["associations.strength = ?", "strong"]     
    end
    
    #fields: user_id, product_id, strength
    class Association < ActiveRecord::Base
      belongs_to :user
      belongs_to :product
    end
    

    And then do something like (on the page)

    <h2>Strongly association users</h2>
    <% @product.strong_associated_users.each do |user| %>
      ...show user info here
    <% end %>
    

    Or, you could not bother with the new has_many associations, and just split the association records up on the page:

    <% grouped = @product.associations.find(:all, :include => [:user]).group_by(&:strength) %>
    <% ["weak", "medium", "strong"].each do |strength| %>
      <% if associations = grouped[strength] %>
        <h2><%= strength %> associations</h2>#
        <% associations.each do |association| %>
          <% user = association.user %>
          ...show user info here
        <% end %>
      <% end %>
    <% end %>