Tuesday, 15 January 2013

Implementing has_and_belongs_to_many association in Rails3


A has_and_belongs_to_many association directly creates a many-to-many connection between models. This blog will illustrate how to implement such relation in Rails3 using this example - if an application includes two models :-
1. Event
2. Organizer
Here each Event can have many Organizers and each Organizer can appear in many Events.

Following are the required steps for implementing such relation :-


1. Generate the models using the command
rails g model Organizer
rails g model Event


Note: you can add necessary attributes to the models.

2. Declare the models in this way

class Event < ActiveRecord::Base
  has_and_belongs_to_many :organizers #append this line to your model
end

class Organizer < ActiveRecord::Base
  has_and_belongs_to_many :events       #append this line to your model
end


3. On running rake db:migrate for these models, by default rails create a column id in its tables. So both Events and Organizers table will have an id column.

4. Generate a rails migration for creating the join table EventsOrganizers.
rails g migration CreateEventsOrganizers

Note: The join table name should have the name of the models in alphabetical order. Here Events come 1st and then Organizers


5. In the migration file created : "create_events_organizers.rb" , write the following code

class CreateEventsOrganizers < ActiveRecord::Migration
  def change
    create_table :events_organizers do |t|
      t.integer :event_id
      t.integer :organizer_id
    end
  end
end



Note: Remove the timestamps (created by default) attribute since it will throw an error on create/edit of the association from the UI.


6. Run rake db:migrate 
This will create a table in your database: events_organizers
Fields will be : idevent_idorganizer_id


7. One has to give an option in the UI for adding multiple organizers to a particular event. So one can add the following code to create a multiple select drop-down for organizers in the view events/_form.html.erb file
<%= f.label :organizers %>
<%= select_tag "organizing_team", options_from_collection_for_select(Organizer.all, 'id', 'name',@event.organizers.map{ |j| j.id }), :multiple => true %>


This code will display a drop-down with the names of already added organizers.


8. In the events_controller.rb file
append these lines to the create and update methods

def create
@event = Event.new(params[:event])
@organizers = Organizer.where(:id => params[:organizing_team])
@event.
organizers << @organizers 
#associate the selected organizers to the event and create records in the join table


def update
@event = Event.find(params[:id])
@organizers = Organizer.where(:id => params[:organizing_team])
@event.organizers.destroy_all   
#disassociate the already added organizers
@event.organizers << @organizers 
#associate the selected organizers to the event and create records in the join table

9. On creation/update of an event when multiple organizers are selected then the entries are added to the join table events_organizers