Reputation: 819
Within my Ruby on Rails application I am trying to implement a relationship between Group and Contact, whereby one group can contain many contacts and one contact can be part of many groups. I am using a model called Contactgroup to deal with this relationship, and so the tables are:
Group (id, name)
Contact (id, firstname, surname)
Contactgroup (group_id, contact_id)
With example data being:
Groups:
ID Name
1 Singers
2 Drummers
Contacts:
ID Firstname Surname
1 Freddy Mercury
2 Roger Taylor
3 Kurt Cobain
4 Dave Grohl
Contact Groups:
Group_ID Contact_ID
1 1
1 3
1 4
2 2
2 4
What I am trying to do is get it so that when a user creates a group, they can select the contacts that they want to add to that group. This means that there is the group form, whereby the user types the group name, and on this form I want to display checkboxes for each of the user's contacts so that the user can select the contacts they want to add to the group, and when they click submit the new group will be saved in the Group table and the new contact group records will be saved in the Contactgroup table.
This is the app/views/groups/_form.html.erb
code:
<%= form_for @group do |f| %>
<% if @group.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(@group.errors.count, "error") %> prohibited this group from being saved:
</h2>
<ul>
<% @group.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<h2>Add members:</h2>
<%= form_for([@group, @group.contactgroups.build]) do |f| %>
<p>
<%= f.collection_check_boxes(:contact_id, @contacts, :id, :firstname) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
On here you can see the code I am trying to use to do this:
<h2>Add members:</h2>
<%= form_for([@group, @group.contactgroups.build]) do |f| %>
<p>
<%= f.collection_check_boxes(:contact_id, @contacts, :id, :firstname) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
I have got this from rails guides (http://guides.rubyonrails.org/getting_started.html) but I get the error undefined method
contactgroups' for #` and don't think this will give me what I want.
My routes file is:
Rails.application.routes.draw do
get 'sessions/new'
get 'sessions/create'
get 'sessions/destroy'
resources :users
get 'welcome/index'
root 'welcome#index'
resources :contacts
resources :groups do
resources :contactgroups
end
resources :contactgroups
get 'sessions/new'
get 'sessions/create'
get 'sessions/destroy'
controller :sessions do
get 'login' => :new
post 'login' => :create
get 'logout' => :destroy
end
end
My groups_controller:
class GroupsController < ApplicationController
def index
@groups = Group.where(user_id: session[:user_id])
end
def show
@group = Group.find(params[:id])
@members = Contactgroup.where(group_id: @group.id)
end
def new
@group = Group.new
@contacts = Contact.where(user_id: session[:user_id])
end
def edit
@group = Group.find(params[:id])
end
def create
@group = Group.new(group_params)
@group.user_id = session[:user_id]
if @group.save
redirect_to @group
else
render 'new'
end
end
def update
@group = Group.find(params[:id])
if @group.update(group_params)
redirect_to @group
else
render 'edit'
end
end
def destroy
@group = Group.find(params[:id])
@group.destroy
redirect_to groups_path
end
private
def group_params
params.require(:group).permit(:name, :user_id)
end
end
And contactgroups_controller:
class ContactgroupsController < ApplicationController
def destroy
@contactgroup = Contactgroup.find(params[:id])
@contactgroup.destroy
redirect_to(:back)
end
end
My models are as follows:
Contact.rb
:
class Contact < ActiveRecord::Base
end
Group.rb
:
class Group < ActiveRecord::Base
end
Contactgroup.rb
:
class Contactgroup < ActiveRecord::Base
belongs_to :contact
belongs_to :group
end
There must be a simple solution to solve this as I assume it is commonly done on other systems, but I am not sure how to do this.
Can someone please help.
Upvotes: 1
Views: 1926
Reputation: 819
This was much simpler than initially thought/suggested.
What I needed to do was change the models to:
Contactgroup
belongs_to :contact
belongs_to :group
Contact
has_many :contactgroups
has_many :groups, through: :contactgroups, :dependent => :destroy
Group
has_many :contactgroups
has_many :contacts, through: :contactgroups, :dependent => :destroy
In the groups_controller
I needed to change the new method and params to:
def new
@group = Group.new
@group.contactgroups.build
end
private
def group_params
params.require(:group).permit(:name, :user_id, { contact_ids: [] })
end
And then add the following line of code into app/views/groups/_form.html.erb
:
<%= f.collection_check_boxes :contact_ids, Contact.where(user_id: session[:user_id]), :id, :firstname ,{ prompt: "firstname" } %>
This provides me with a checkbox for each contact, and allows contactgroup records to be created from the group form.
Upvotes: 1
Reputation: 27747
Ok so the issue is very simple. You are calling @group.contactgroups
but you haven't actually set up that association on the group
model yet. only have associations set up from the contactgroup
side. So you can do contactgroup.group
but not group.contactgroups
Your best bet is to actually model this as habtm - as I mentioned earlier. This is how you'd do that:
Contact.rb:
class Contact < ActiveRecord::Base
has_and_belongs_to_many :groups
end
Group.rb:
class Group < ActiveRecord::Base
has_and_belongs_to_many :contacts
end
Note: you still have the concept of the contact-group for HABTM but using Rails standard naming it would be in your database as the contacts_groups
table. Then you could build your forms that way.
With a quick google, here's a S/O question on using checkboxes with HABTM (haven't vetted it for usefulness to your situation): Rails 4 - checkboxes for has_and_belongs_to_many association
Using HABTM is Rails standard practice for lots of very good reasons. It really does actually fit your situation (honest!) and it does not actually break the requirement you have of wanting to see it in the SQL (seriously!).
Give it a try first :) I can tell you how to break Rails conventions... but it's generally well-understood that you shouldn't break conventions until you know what the conventions are there for.
Upvotes: 0
Reputation: 421
You cannot use form inside form. The correct way to use collection_check_boxes
is following.
Replace
<%= form_for([@group, @group.contactgroups.build]) do |f| %>
<p>
<%= f.collection_check_boxes(:contact_id, @contacts, :id, :firstname) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
With just
<p>
<%= f.collection_check_boxes(:contact_ids, @contacts, :id, :firstname) %>
</p>
Upvotes: 1