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 : id, event_id, organizer_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
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
9. On creation/update of an event when multiple organizers are selected then the entries are added to the join table events_organizers