Polymorphic form--多态表单
一个ruby on rails项目,用户和公司的模型都有地址。
我要创建一个地址表,包含用户和公司表的引用,比直接做下去要好一点,这回让我的数据库设计保持干净。
我的第一印象是,这似乎很难实现,外面所有的讨论及教程都只说明了在model如何设置,但是并没有说明在controller和view如何使用它。我好一顿放狗,也没有得到太多的帮助。
令我感到惊喜是其实在rails设置并使用多态表单是很简单的。
首先依然是先设置model结构:
01class Company< ActiveRecord::Base
02 has_one :address, :as =>; :addressable, :dependent => :destroy
03end
04
05class User < ActiveRecord::Base
06 has_one :address, :as => :addressable, :dependent => :destroy
07end
08
09class Address < ActiveRecord::Base
10 belongs_to :addressable, :polymorphic => true
11end
接下来是创建一个Address表来保存地址:
01class CreateAddresses < ActiveRecord::Migration
02 def self.up
03 create_table :addresses do |t|
04 t.string :street_address1, :null => false
05 t.string :street_address2
06 t.string :city, :null => false
07 t.string :region, :null => false
08 t.string :postcode, :null => false, :limit => 55
09 t.integer :addressable_id, :null => false
10 t.string :addressable_type, :null => false
11
12 t.timestamps
13 end
14 end
15
16 def self.down
17 drop_table :addresses
18 end
19end
接下来是controller,你只需要修改controller中的"new","create","edit","update"四个action,好让需要的时候可以访问和修改address。
01class CompaniesController < ApplicationController
02
03 def new
04 @company = Company.new
05 @company.address = Address.new
06 end
07
08 def edit
09 @company = Company.find(params[:id])
10 @company.address = Address.new unless @company.address != nil
11 end
12
13 def create
14 @company = Company.new(params[:company])
15 @company.address = Address.new(params[:address])
16
17 if @company.save
18 @company.address.save
19 flash[:notice] = 'Company was successfully created.'
20 redirect_to(@company)
21 else
22 render :action => 'new'
23 end
24 end
25
26 def update
27 @company = Company.find(params[:id])
28
29 if @company.update_attributes(params[:company])
30 @company.address.update_attributes(params[:address])
31 flash[:notice] = 'Company was successfully updated.'
32 redirect_to(@company)
33 else
34 render :action => 'edit'
35 end
36 end
37end
最后一件事是让address在表单中可以正常工作,我们这里使用field_for方法:
01<% form_for(@company) do |f| %>
02 <%= f.error_messages %>
03<dl>
04 <%= f.text_field :name %>
05 <%= f.text_field :telephone %>
06 <%= f.text_field :fax %>
07 <%= f.text_field :website_url %>
08 </dl>
09
10 <% fields_for(@company.address) do |address_fields| %>
11 <%= address_fields.hidden_field :addressable_id %>
12 <%= address_fields.hidden_field :addressable_type %>
13<dl>
14 <%= address_fields.text_field :street_address1 %>
15 <%= address_fields.text_field :street_address2 %>
16 <%= address_fields.text_field :city %>
17 <%= address_fields.text_field :region %>
18 <%= address_fields.text_field :postcode %>
19 </dl>
20
21 <% end %>
22<% end %>
到这就应该可以正常工作了。
有人要问了,如果我去的了address对象,能否反向取得Company或者User对象呢?答案当然是肯定的。
1@address = Address.find(params[:id])
2@address.addressable
3这样就可以访问了。