accepts_nested_attributes_for在提交参数时为nil对象提供未定义的方法,但仅当不是每个复选框都被选中时?

时间:2011-12-07 02:02:51

标签: ruby-on-rails ruby ruby-on-rails-3

我不知道为什么这不起作用,但我似乎无法通过复杂的形式来创建必要的连接模型。

因此,一个站点可以通过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

这里很标准的东西。现在这是我的控制器。我正在做的是询问数据库有多少个环境,并为新站点构建那么多的siteenvs。这样,表单将为每个环境提供一个复选框,因此用户可以选择他们想要的任何内容。

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

然后我的新网站表单如下:

位点/ 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 %>

所以会发生什么,我的表单呈现正确,并且所有HTML属性看起来都是正确的。复选框的值是相应环境的ID,所有名称都显示为Rails生成的HTML:

表单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>

但是,除非您标记每个复选框,否则我会在siteenvs模型上引用某个方法时出错。我想这是因为提交的参数引用了一个不存在的环境_(0),但我认为rails会忽略它。事实上,如果没有检查环境标识,我甚至不确定为什么会提交这些环境标识!这是一个示例输出,当我只选择一个复选框时,5:

提交了参数

{"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"}

所以我不知道0来自哪里,也不知道为什么未选中的复选框甚至包含在参数中。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

这直接来自表单check_box方法的文档:

  

返回为访问指定属性而定制的复选框标记   (通过方法识别)分配给模板的对象   (由对象确定)。该对象必须是实例对象   (@object)而不是本地对象。这意味着该方法返回一个   整数,如果该整数大于零,则复选框为   检查。输入标记上的其他选项可以作为哈希传递   有选项。 checked_value默认为默认值1   unchecked_value设置为0,便于布尔值。

     

<强>疑难杂症

     

HTML规范说未经检查的复选框不成功,   因此Web浏览器不会发送它们。不幸的是这引入了   问题:如果发票模型有付费标志,并且形式为   编辑付费发票,用户取消选中其复选框,不付款   参数被发送。所以,任何像

这样的质量分配习语

@invoice.update_attributes(params[:invoice])

  

不会更新旗帜。

从代码的参数中我看到四个environment_id => "0"是复选框的未选中值。您需要在处理数据时过滤掉这些数据。