什么是嵌套表单呢?举个简单的例子吧,比如你有两个表,一个User表,另一个Account表,他们是一对一的关系(也可以一对多等)。现在需要提交一个表单的时候同时提交User,Account对应的字段数据。在Rails中有一种简单的方法解决,分别使用了这些方法
accepts_nested_attributes_for
attr_accessible
fields_for
下面我们用代码进行说明一下。
class User < ActiveRecord::Base
has_one :account
accepts_nested_attributes_for :account #注意添加这两行
attr_accessible :account_attributes
end
class Account<ActiveRecord::Base
belongs_to :user
class UsersController < ApplicationController
def new
@user = User.new
@user.account.build #不要遗漏
end
def create
@user = User.new(params[:user])
if @user.save
...
end
end
end
users/new.html.erb:
<% form_for @user do |f| %>
<%= f.text_field :name %>
<% f.fields_for :account do |pf| %> #注意这里
<%= pf.text_field :age %>
<% end %>
<% end %>
这是一对一的情况,假设是一对多呢,如User has_many :accounts ; Account belongs_to :user这种情况下只要将对应的account改写成accounts,就ok了
一些深入参考:
http://robots.thoughtbot.com/post/52960938209/accepts-nested-attributes-for-with-has-many-throughhttp://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
以上是Rails3中,如果在Rails4中使用了Using Strong Parameters ,参考这里
去掉model中的 attr_accessible :account_attributes
,在对应的controller中添加Strong Parameters的方法
实例1:
coupon.rb中:
accepts_nested_attributes_for :cdkey, allow_destroy: true
作用是添加一对一cdkey表单的嵌套
_form.html.erb中:
div class="ui form">
<%= form_for [:manage, @coupon], html: {multipart: true} do |f| %>
<div class="fields">
<div class="field">
<%= f.label :coupon_type %>
<%= f.select :coupon_type, Coupon::CouponType, {include_blank: "select coupon type"}, class: "ui dropdown" %>
</div>
<div class="field">
<%= f.label :total_count, "数量限制" %>
<%= f.text_field :total_count %>
</div>
</div>
<div class="ui segment">
<h4 class="ui header">CDKEY</h4>
<div class="fields">
<%= f.fields_for :cdkey do |c| %>
<div class="field">
<%= c.label :code, "cdkey 设置后可以输入该cdkey兑换优惠券" %>
<%= c.text_field :code %>
</div>
<div class="field">
<%= c.label :count, "该cdkey可以兑换的次数" %>
<%= c.text_field :count %>
</div>
<div class="field">
<%= c.label :start_at %>
<%= c.text_field :start_at, class: "datepicker" %>
</div>
<div class="field">
<%= c.label :end_at %>
<%= c.text_field :end_at, class: "datepicker" %>
</div>
<div class="field">
<label for="">Delete</label>
<div class="ui checkbox">
<%= c.check_box :_destroy, style: "display: hidden" %>
<%= label_tag "删除" %>
</div>
</div>
<% end %>
</div>
</div>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_area :description, rows: 2 %>
</div>
<div class="fields">
<div class="field">
<%= f.label :start_time %>
<%= f.text_field :start_time, class: "datepicker" %>
</div>
<div class="field">
<%= f.label :end_time %>
<%= f.text_field :end_time, class: "datepicker" %>
</div>
</div>
<div class="fields">
<div class="field">
<%= f.label :active_days %>
<%= f.text_field :active_days %>
</div>
<div class="field">
<%= f.label :expiration_time %>
<%= f.text_field :expiration_time, class: "datepicker" %>
</div>
</div>
<div class="fields">
<div class="field">
<%= f.label :value %>
<%= f.text_field :value %>
</div>
<div class="field">
<%= f.label :status %>
<%= f.select :status, Coupon::STATUS, {}, class: "ui dropdown" %>
</div>
</div>
<div class="fields">
<div class="field">
<%= f.label :no_prefix %>
<%= f.text_field :no_prefix %>
</div>
</div>
<div class="ui segment">
<h4 class="ui header">Can use countries</h4>
<div class="ui grid">
<div class="ui wide column">
<div class="ui checkbox">
<%= check_box_tag "coupon[countries_limit][]", "all", @coupon.countries_limit.to_a.include?("all"), style: "display: hidden" %>
<%= label_tag "all" %>
</div>
</div>
<% SupportCountry.all.each do |sc| %>
<div class="ui wide column">
<div class="ui checkbox">
<%= check_box_tag "coupon[countries_limit][]", sc.iso_code, @coupon.countries_limit.to_a.include?(sc.iso_code), style: "display: hidden" %>
<%= label_tag sc.iso_code %>
</div>
</div>
<% end %>
</div>
</div>
<div class="field">
<button class="ui submit blue button" type="submit" name="action">
Submit
</button>
</div>
<% end %>
</div>
<%= form_for [:manage, @coupon], html: {multipart: true} do |f| %>
<div class="fields">
<div class="field">
<%= f.label :coupon_type %>
<%= f.select :coupon_type, Coupon::CouponType, {include_blank: "select coupon type"}, class: "ui dropdown" %>
</div>
<div class="field">
<%= f.label :total_count, "数量限制" %>
<%= f.text_field :total_count %>
</div>
</div>
<div class="ui segment">
<h4 class="ui header">CDKEY</h4>
<div class="fields">
<%= f.fields_for :cdkey do |c| %>
<div class="field">
<%= c.label :code, "cdkey 设置后可以输入该cdkey兑换优惠券" %>
<%= c.text_field :code %>
</div>
<div class="field">
<%= c.label :count, "该cdkey可以兑换的次数" %>
<%= c.text_field :count %>
</div>
<div class="field">
<%= c.label :start_at %>
<%= c.text_field :start_at, class: "datepicker" %>
</div>
<div class="field">
<%= c.label :end_at %>
<%= c.text_field :end_at, class: "datepicker" %>
</div>
<div class="field">
<label for="">Delete</label>
<div class="ui checkbox">
<%= c.check_box :_destroy, style: "display: hidden" %>
<%= label_tag "删除" %>
</div>
</div>
<% end %>
</div>
</div>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_area :description, rows: 2 %>
</div>
<div class="fields">
<div class="field">
<%= f.label :start_time %>
<%= f.text_field :start_time, class: "datepicker" %>
</div>
<div class="field">
<%= f.label :end_time %>
<%= f.text_field :end_time, class: "datepicker" %>
</div>
</div>
<div class="fields">
<div class="field">
<%= f.label :active_days %>
<%= f.text_field :active_days %>
</div>
<div class="field">
<%= f.label :expiration_time %>
<%= f.text_field :expiration_time, class: "datepicker" %>
</div>
</div>
<div class="fields">
<div class="field">
<%= f.label :value %>
<%= f.text_field :value %>
</div>
<div class="field">
<%= f.label :status %>
<%= f.select :status, Coupon::STATUS, {}, class: "ui dropdown" %>
</div>
</div>
<div class="fields">
<div class="field">
<%= f.label :no_prefix %>
<%= f.text_field :no_prefix %>
</div>
</div>
<div class="ui segment">
<h4 class="ui header">Can use countries</h4>
<div class="ui grid">
<div class="ui wide column">
<div class="ui checkbox">
<%= check_box_tag "coupon[countries_limit][]", "all", @coupon.countries_limit.to_a.include?("all"), style: "display: hidden" %>
<%= label_tag "all" %>
</div>
</div>
<% SupportCountry.all.each do |sc| %>
<div class="ui wide column">
<div class="ui checkbox">
<%= check_box_tag "coupon[countries_limit][]", sc.iso_code, @coupon.countries_limit.to_a.include?(sc.iso_code), style: "display: hidden" %>
<%= label_tag sc.iso_code %>
</div>
</div>
<% end %>
</div>
</div>
<div class="field">
<button class="ui submit blue button" type="submit" name="action">
Submit
</button>
</div>
<% end %>
</div>
注意: <%= f.fields_for :cdkey do |c| %>
<div class="field">
<%= c.label :code, "cdkey 设置后可以输入该cdkey兑换优惠券" %>
<%= c.text_field :code %>
</div>
<div class="field">
<%= c.label :count, "该cdkey可以兑换的次数" %>
<%= c.text_field :count %>
</div>
<div class="field">
<%= c.label :start_at %>
<%= c.text_field :start_at, class: "datepicker" %>
</div>
<div class="field">
<%= c.label :end_at %>
<%= c.text_field :end_at, class: "datepicker" %>
</div>
<div class="field">
<label for="">Delete</label>
<div class="ui checkbox">
<%= c.check_box :_destroy, style: "display: hidden" %>
<%= label_tag "删除" %>
</div>
</div>
<% end %>
<div class="field">
<%= c.label :code, "cdkey 设置后可以输入该cdkey兑换优惠券" %>
<%= c.text_field :code %>
</div>
<div class="field">
<%= c.label :count, "该cdkey可以兑换的次数" %>
<%= c.text_field :count %>
</div>
<div class="field">
<%= c.label :start_at %>
<%= c.text_field :start_at, class: "datepicker" %>
</div>
<div class="field">
<%= c.label :end_at %>
<%= c.text_field :end_at, class: "datepicker" %>
</div>
<div class="field">
<label for="">Delete</label>
<div class="ui checkbox">
<%= c.check_box :_destroy, style: "display: hidden" %>
<%= label_tag "删除" %>
</div>
</div>
<% end %>
这一段为对嵌套表单的提交
coupon_controller.rb中:
def edit
@coupon = Coupon.find(params[:id])
if @coupon.cdkey.present?
@cdkey =@coupon.cdkey
else
@cdkey = @coupon.build_cdkey
end
end
@coupon = Coupon.find(params[:id])
if @coupon.cdkey.present?
@cdkey =@coupon.cdkey
else
@cdkey = @coupon.build_cdkey
end
end
def update
@coupon = Coupon.find(params[:id])
if @coupon.update(coupon_params)
redirect_to manage_coupons_path
end
end
@coupon = Coupon.find(params[:id])
if @coupon.update(coupon_params)
redirect_to manage_coupons_path
end
end
private
def coupon_params
params.require(:coupon).permit(
:name, :coupon_type, :value, :description, :start_time, :end_time,
:active_days, :expiration_time, :status, :no_prefix, :total_count, cdkey_attributes: [:id, :code, :start_at, :end_at, :count, :_destroy],
countries_limit: []
)
end
def coupon_params
params.require(:coupon).permit(
:name, :coupon_type, :value, :description, :start_time, :end_time,
:active_days, :expiration_time, :status, :no_prefix, :total_count, cdkey_attributes: [:id, :code, :start_at, :end_at, :count, :_destroy],
countries_limit: []
)
end
注意:edit中的
@cdkey = @coupon.build_cdkey这句和coupon_params健壮参数中 cdkey_attributes: [:id, :code, :start_at, :end_at, :count, :_destroy],这句