2009-09-17 69 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

相關問題