A collection of computer systems and programming tips that you may find useful.
 
Brought to you by Craic Computing LLC, a bioinformatics consulting company.

Tuesday, November 5, 2013

HABTM relationships in Rails 4

Rails 4 has changed the way has_and_belongs_to_many (HABTM) relationships are handled as well as introducing strong parameters in the controller as a replacement to having :attr_accessible in your model.

Here is how you set up HABTM with a joining table.

My models are Tutorial and Category - a tutorial can have more than one category and vice versa.

Both models already existed and I wanted to create the relationship between them.

1: Create a migration for the joining table

The default table name should be the plural of the two models, in alphabetical order, separated by an underscore. (You can give it any name you want - you just need to include that later in the model)

class CreateCategoriesTutorials < ActiveRecord::Migration
  def change
    create_table :categories_tutorials do |t|
      t.belongs_to :tutorial
      t.belongs_to :category
    end
  end
end

2: Add the relationships to the models

Previously you would have done this (in the Tutorial model):

has_many :categories_tutorials, :dependent => :destroy
has_many :categories, :through => :categories_tutorials, :uniq => true

That is now simplified to this:

has_and_belongs_to_many :categories

If you have a custom table name then specifiy it like this:

has_and_belongs_to_many :categories, join_table: :my_category_tutorial_table

The Category model has:

has_and_belongs_to_many :tutorials

The CategoryTutorial model has:

belongs_to :category
belongs_to :tutorial

In my example I want to specify Categories when I create a Tutorial. 

In order for this to work I need to allow the tutorials_controller to accept the category_ids parameter which is passed from the tutorial form.

At the bottom of the controller Rails has created this:

def tutorial_params
    params.require(:tutorial).permit(:title, :description, :status)
end

I need to add category_ids and have it set up as an array 

def tutorial_params
    params.require(:tutorial).permit(:title, :description, :status, :category_ids => [])
end

That last step is critical - if you miss this then the associated rows are not created in the database.

For more information on HABTM take a look at:

http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association


1 comment:

Matt said...

Could I please see the whole controller? I think I am missing the point in the create and edit methods. Thanks! I'm trying to do something similar.

Archive of Tips