I have no idea why this isn’t working, but I can’t seem to get a complex form to create my necessary join models.
So a site can have many environments, through siteenvs.
site.rb
class Site < ActiveRecord::Base
has_many :siteenvs, :dependent => :destroy
has_many :environments, :through => :siteenvs, :uniq => true
attr_accessible :name, :description, :documentation, :protocol, :port, :siteenvs_attributes
accepts_nested_attributes_for :siteenvs, :reject_if => proc { |attributes| attributes["environment_id"] == "0"}
end
siteenv.rb
class Siteenv < ActiveRecord::Base
belongs_to :site
belongs_to :environment
validates :site_id, :uniqueness => { :scope => :environment_id }
end
environment.rb
class Environment < ActiveRecord::Base
has_many :siteenvs, :dependent => :destroy
has_many :sites, :through => :siteenvs, :uniq => true
end
Pretty standard stuff here. Now here is my controller. What I’m doing is asking the database how many Environments there are, and building that many siteenvs for a new site. This is so that the form will have one checkbox for each environment, so a user can choose whatever they would like.
sites_controller.rb
class SitesController < ApplicationController
load_and_authorize_resource
respond_to :html, :xml, :json
def new
@site = Site.new
Environment.all.each do |env|
siteenv = @site.siteenvs.build(:environment_id => env.id)
end
respond_with (@site)
end
...
def create
@site = Site.new(params[:site])
if @site.save
flash[:success] = "New site created."
respond_with(@site, :location => @site)
else
render 'new'
end
end
end
And then my new site form looks like this:
sites/new.html.erb
<h1>Create a new site</h1>
<%= form_for(@site) do |f| %>
<h3>Step 1 - General</h3>
<table>
<%= render 'site_fields', :f => f %>
</table>
<h3>Step 2 - Environments</h3>
<div id="environments">
<ul>
<%= f.fields_for :siteenvs do |builder| %>
<% environment = Environment.find(builder.object.environment_id) %>
<li><%= builder.check_box :environment_id, { :checked => false, :class => "s_environment_checkbox", :env => environment.name, :domain => environment.domain }, builder.object.environment_id %>
<%= builder.label :environment_id, "#{environment.name}" %>
</li>
<% end %>
</ul>
</div>
<div class="actions">
<%= f.submit "Finish" %>
</div>
<% end %>
So what happens is that my form renders correctly, and all the HTML attributes appear to be correct. The value of the checkboxes is the ID of the corresponding environment, and the names all appear to be happy Rails generated HTML:
form HTML
<li>
<input name="site[siteenvs_attributes][0][environment_id]" type="hidden" value="0">
<input class="s_environment_checkbox" domain="example.com" env="env1" id="site_siteenvs_attributes_0_environment_id" name="site[siteenvs_attributes][0][environment_id]" type="checkbox" value="1">
</li>
However, unless you mark every single checkbox, I get an error referring to some method on the siteenvs model. I imagine this is due to the fact that submitted parameters are referencing an environment_id that doesn’t exist (0), but I would think that rails would ignore this. In fact, I’m not even sure why those environment_ids are being submitted if they aren’t checked! Here is some example output from when I only chose one checkbox, out of 5:
submitted params
{"utf8"=>"✓",
"authenticity_token"=>"Q1zdbb/ibcJrKb2FYf45o+Q43o2PZm1UU75dngis1UE=",
"site"=>{"name"=>"bob",
"description"=>"",
"documentation"=>"",
"protocol"=>"http",
"port"=>"80",
"siteenvs_attributes"=>{"0"=>{"environment_id"=>"1",
"url"=>""},
"1"=>{"environment_id"=>"0"},
"2"=>{"environment_id"=>"0"},
"3"=>{"environment_id"=>"0"},
"4"=>{"environment_id"=>"0"}}},
"commit"=>"Finish"}
So I have no idea where that 0 came from, nor why the unselected checkboxes are even being including in the params. Any ideas?
This is straight from the documentation for the check_box method for forms:
@invoice.update_attributes(params[:invoice])From the params of your code I see that the four
environment_id => "0"are the unchecked value of the checkbox. You will want to filter out those when you handle the data.