2009-09-17 71 views
4

我在写一个简单的Rails模型,名为Person has_many :phone_numbers,我试图将手机号码保存为复杂的形式,而无需手动编写setter方法。 accepts_nested_attributes_for应该做我想要的,但我无法使其正常工作。下面的代码我到目前为止:使用Rails模型与accept_nested_attributes_for

迁移

class CreatePeople < ActiveRecord::Migration 
    def self.up 
    create_table :people do |t| 
     t.string :first_name 
     t.string :last_name 
     t.integer :address_id  
     t.string :email 

     t.timestamps 
    end 
    end 

    def self.down 
    drop_table :people 
    end 
end 

class CreatePhoneNumbers < ActiveRecord::Migration 
    def self.up 
    create_table :phone_numbers do |t| 
     t.string :number, :limit => 10 
     t.string :extension, :limit => 5 
     t.string :description, :null => false 
     t.integer :telephone_id 
     t.string :telephone_type 

     t.timestamps 
    end 
    end 

    def self.down 
    drop_table :phone_numbers 
    end 
end 

模式

class Person < ActiveRecord::Base 
    has_one :address, :as => :addressable, :dependent => :destroy 
    has_many :phone_numbers, 
    :as => :telephone, 
    :dependent => :destroy 

    accepts_nested_attributes_for :phone_numbers 

    attr_protected :id 

    validates_presence_of :first_name, :last_name, :email 
end 

class PhoneNumber < ActiveRecord::Base 
    attr_protected :id 

    belongs_to :telephone, :polymorphic => true 
end 

查看

<% form_for @person, :builder => CustomFormBuilder do |f| %> 
    <%= f.error_messages %> 

    <%= f.text_field :first_name %> 
    <%= f.text_field :last_name %> 

    <% fields_for "person[address]", @person.address, :builder => CustomFormBuilder do |ff| %> 
    <%= ff.text_field :address_1 %> 
    <%= ff.text_field :address_2 %> 
    <%= ff.text_field :city %> 
    <%= ff.text_field :state %> 
    <%= ff.text_field :zip %> 
    <% end %> 

    <h2>Phone Numbers</h2> 
    <% @person.phone_numbers.each do |phone_number| %> 
    <% fields_for "person[phone_numbers][]", phone_number, :builder => CustomFormBuilder do |ff| %> 
     <%= ff.text_field :description %> 
     <%= ff.text_field :number %> 
     <%= ff.text_field :extension %> 
    <% end %> 
    <% end %> 

    <%= f.text_field :email %> 

    <%= f.submit 'Create' %> 

<% end %> 

控制器

def new 
    @person = Person.new 
    @person.build_address 
    @person.phone_numbers.build 

    respond_to { |format| format.html } 
end 

def create 
    @person = Person.new(params[:person]) 

    respond_to do |format| 
    if @person.save 
     flash[:notice] = "#{@person.name} was successfully created." 
     format.html { redirect_to(@person) } 
    else 
     format.html { render :action => 'new' } 
    end 
    end 
end 

我已经验证正在创建一个PHONE_NUMBERS =方法,但后还是引起了:

PhoneNumber(#69088460) expected, got HashWithIndifferentAccess(#32603050) 

RAILS_ROOT: H:/projects/test_project 

C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/associations/association_proxy.rb:263:in `raise_on_type_mismatch' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/associations/association_collection.rb:319:in `replace' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/associations/association_collection.rb:319:in `each' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/associations/association_collection.rb:319:in `replace' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/associations.rb:1290:in `phone_numbers=' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2740:in `send' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2740:in `attributes=' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2736:in `each' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2736:in `attributes=' 
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2434:in `initialize' 
H:/projects/salesguide/app/controllers/accounts_controller.rb:46:in `new' 
H:/projects/test_project/app/controllers/accounts_controller.rb:46:in `create' 

我能得到这个通过手动写PHONE_NUMBERS =方法来工作,但这会造成巨大的工作重复,我宁愿学习如何做到这一点。任何人都可以看到我在做什么错了吗?

回答

6

您忘记调用fields_for作为人形式的方法。否则,你实际上并没有在accept_nested_attributes_for上下文中使用fields_for。 Michael的解决方案试图欺骗Rails将提交视为正确定义的accep_nested_attributes_for表单。

为你正在尝试做正确的语法是:

parent_form_object.fields_for id, object_containing_values, {form_for options}, &block 

你会找到你的代码看起来更清洁和更简单的调试,如果你提供一个符号作为ID,包括孩子的关联名模型在您的人员模型中定义。

另外,如果@ person.phone_numbers为空,则您使用的每个块可能会导致问题。您可以确保至少有一组电话号码字段的类似于我

<% @phs = @person.phone_numbers.empty? ? @person.phone_numbers.build : @person.phone_numbers %> 

用于所有改正的一行,该代码会做你希望它是什么。

查看

<% form_for @person, :builder => CustomFormBuilder do |f| %> 
    <%= f.error_messages %> 

    <%= f.text_field :first_name %> 
    <%= f.text_field :last_name %> 

    <% f.fields_for :address, @person.address, :builder => CustomFormBuilder do |address_form| %> 
    <%= address_form.text_field :address_1 %> 
    <%= address_form.text_field :address_2 %> 
    <%= address_form.text_field :city %> 
    <%= address_form.text_field :state %> 
    <%= address_form.text_field :zip %> 
    <% end %> 

    <h2>Phone Numbers</h2> 
    <% @phs = @person.phone_numbers.empty? ? @person.phone_numbers.build : @person.phone_numbers %> 
    <% f.fields_for :phone_numbers, @phs, :builder => CustomFormBuilder do |phone_number_form| %> 
     <%= phone_number_form.text_field :description %> 
     <%= phone_number_form.text_field :number %> 
     <%= phone_number_form.text_field :extension %> 
    <% end %> 

    <%= f.text_field :email %> 

    <%= f.submit 'Create' %> 

<% end %> 

你可能会发现它很有用,为工作示例检查出complex-form-examples repository on github。它还附带代码,以便从视图/表单动态添加新条目:has_many关系。

4

昨天我在玩弄accepts_nested_attributes_for,想弄清楚Rails form with three models and namespace。我需要设置表单略有不同,请尝试使用:person[phone_numbers_attributes][]

+0

我不敢相信,在我查阅的所有文档中,没有更清楚地指出。谢谢。 – 2009-09-17 22:42:35

相关问题