Learn Rails - Many To Many Relationships

Question Click to View Answer

Create a new rails project called many_to_many_relationships and cd into the directory.

$ rails new many_to_many_relationships
$ cd many_to_many_relationships

Create a mountain model with a name attribute. Create a climber model with a name attributes. Associate the models with a join table.

$ rails g model mountain name
$ rails g model climber name
$ rails g migration climbers_mountains mountain_id:integer climber_id:integer
# Update the climbers_mountains migration file as follows:
class ClimbersMountains < ActiveRecord::Migration
  def change
    create_table :climbers_mountains, :id => false do |t|
      t.integer :climber_id
      t.integer :mountain_id
    end
  end
end

Associate the models.

# models/mountain.rb
class Mountain < ActiveRecord::Base
  attr_accessible :name
  has_and_belongs_to_many :climbers
end

# models/climber.rb
class Climber < ActiveRecord::Base
  attr_accessible :name
  has_and_belongs_to_many :mountains
end

Use Rails console to create a climber and a mountain and associate the climber and the mountain.

>> antonio = Climber.create!(:name => "Antonio")
>> heavenly = Mountain.create!(:name => "Heavenly")
This command associates antonio and heavenly:
>> antonio.mountains << heavenly

# We can run these commands now that antonio and heavenly are associated:
>> antonio.mountains
>> heavenly.mountains

Inspect the join table in the database and see how Rails stores the data.

$ rails db
sqlite> select * from climbers_mountains;

Use Rails console to associate another mountain and climber (use the reverse direction that you used in the previous example).

>> big_mountain = Mountain.create!(:name => "Big")
>> big_mountain.climbers << antonio

Let's say we want to add a duration column to the climbers_mountains table. What are the associated complications with this?

It is straightforward to generate a migration and add this column to the climbers_models table, but how will we update this attribute? There is no climbers_models Class, so this would be awkward.

Generate a magazine model with a name attribute. Generate a reader model with a name attribute. Generate a join model with type:string and length:integer attributes. Create the tables in the database.

$ rails g model magazine name
$ rails g model reader name
$ rails g model subscription type length:integer magazine_id:integer reader_id:integer
$ rake db:migrate

Associate the models.

# models/magazine.rb
class Magazine < ActiveRecord::Base
  attr_accessible :name
  has_many :subscriptions
  has_many :readers, :through => :subscriptions
end

# models/reader.rb
class Reader < ActiveRecord::Base
  attr_accessible :name
  has_many :subscriptions
  has_many :magazines, :through => :subscriptions
end

models/subscription.rb
class Subscription < ActiveRecord::Base
  attr_accessible :length, :type
  belongs_to :reader
  belongs_to :magazine
end

Use Rails console to associate a magazine and a reader. Give examples of how the associations can be used.

>> vogue = Magazine.create!(:name => "Vogue")
>> bob = Reader.create!(:name => "Bob")
>> bob.magazines << vogue

# Now that the models are associated, you can use these commands:
>> bob.magazines
>> vogue.readers
>> bob.subscriptions
>> vogue.subscriptions

What is a huge advantage of has_many through relationships over has_and_belongs_to_many?

Has_many through is much more flexible and gives you access to a join model that you can add columns to and customize like any other model. A join model is much more powerful and has the same features as a has_and_belongs_to_many relationship, so it is almost always better to use a has_many :through relationship.

What should you do when naming a join model?

Think of a single word (if possible) that describes the relationship. Don't name it something boring, like climbers_mountains - think of something better like "adventures". Most of these relationships end with "ment", "tion", or "ship".

What is a good name for a join model between a person model and a company model?

Employment. Notice that this join model would not be called employee because employee describes the person, not the relationship between the person and the company. Employment is a more accurate description of the relationship between the person and the company.

What is a good name for a join model for a relationship between a user model and a group model.

Membership