はじめに
モデル間をアソシエーションしている際、関連づけられているモデルオブジェクトを自動的に保存したり、保存しないようにするにはautosave
オプションを利用します。
このオプションの挙動について、下記のサンプルを用いて検証をしました。
サンプル
モデル
CustomerモデルとAddressモデルを1対1で関連づけしています。
app/models/customer.rb
class Customer < ActiveRecord::Base
has_one :address, dependent: :destroy
validates :name, presence: true
end
app/models/address.rb
class Address < ActiveRecord::Base
belongs_to :customer
validates :address, presece: true
end
フォーム
関連づけられたモデルを一つの画面で登録したり、編集したりするため、フォームオブジェクトを作成します。
app/forms/customer_form.rb
class CustomerForm
include ActiveModel::Model
attr_accessor :customer
delegate :persisted?, to: :customer
def initialize(customer = nil)
@customer = customer
@customer ||= Customer.new
@customer.build_address unless @customer.address
end
def assign_attributes(params = {})
@params = params
customer.assign_attributes(customer_params)
customer.address.assign_attributes(address_params)
end
def save
customer.save
end
private
def customer_params
@params.require(:customer).permit(:name)
end
def address_params
@params.require(:address).permit(:address)
end
end
ビュー
パーシャルをつかってビューを作成します。
app/views/customers/new.html.erb
<% @title = '顧客の新規登録' %>
<h1><%= @title %></h1>
<div id="generic-form">
<%= form_for @customer_form, as: 'form', url: :customers do |f| %>
<%= render 'form', f: f %>
<div class="buttons">
<%= f.submit '登録' %>
<%= link_to 'キャンセル', :customers %>
</div>
<% end %>
</div>
app/views/customers/edit.html.erb
<% @title = '顧客アカウントの編集' %>
<h1><%= @title %></h1>
<div id="generic-form">
<%= form_for @customer_form, as: 'form',
url: [ @customer_form.customer ] do |f| %>
<%= render 'form', f: f %>
<div class="buttons">
<%= f.submit '更新' %>
<%= link_to 'キャンセル', :customers %>
</div>
<% end %>
</div>
app/views/customers/_form.html.erb
<fieldset id="customer-fields">
<legend>基本情報</legend>
<%= render 'customer_fields', f: f %>
</fieldset>
<fieldset id="address-fields">
<legend>自宅住所</legend>
<%= render 'address_fields', f: f %>
</fieldset>
app/views/customers/_customer_fields.html.erb
<%= f.fields_for :customer, f.object.customer do |ff| %>
<div>
<%= ff.label :name, '氏名', class: 'required' %>
<%= ff.text_field :name, required: true %>
</div>
app/views/customers/_address_fields.html.erb
<%= f.fields_for :address, f.object.customer.address do |ff| %>
<div>
<%= ff.label :address, '住所', class: 'required' %>
<%= ff.text_field :address, required: true %>
</div>
コントローラ
新規登録と編集用のコントローラを作成します。
app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def new
@customer_form = CustomerForm.new
end
def edit
@customer_form = CustomerForm.new(Customer.find(params[:id]))
end
def create
@customer_form = CustomerForm.new
@customer_form.assign_attributes(params[:form])
if @customer_form.save
flash.notice = '顧客を追加しました。'
redirect_to action: 'index'
else
flash.now.alert = '入力に誤りがあります。'
render action: 'new'
end
end
def update
@customer_form = CustomerForm.new(Customer.find(params[:id]))
@customer_form.assign_attributes(params[:form])
if @customer_form.save
flash.notice = '顧客情報を更新しました。'
redirect_to action: 'index'
else
flash.now.alert = '入力に誤りがあります。'
render action: 'edit'
end
end
end
検証1(autosaveオプションなし)
モデルにautosaveオプションを利用しない場合(つまり上記サンプルの状態)の検証です。
- 新規登録の場合: CustomerオブジェクトとAddressオブジェクトの両方がデータベースに保存される。
- 更新の場合: Csutomerオブジェクトの変更のみデータベースに保存され、Addressオブジェクトの変更はデータベースに保存されない。
検証2(autosaveオプションにtrueを指定)
モデルにautosaveオプションをtrueで指定した場合の検証です。
app/models/customer.rb
class Customer < ActiveRecord::Base
has_one :address, dependent: :destroy, autosave: true
validates :name, presence: true
end
- 新規登録の場合: CustomerオブジェクトとAddressオブジェクトの両方がデータベースに保存される。
- 更新の場合: CustomerオブジェクトとAddressオブジェクトの両方の変更がデータベースに保存される。
検証3(autosaveオプションにfalseを指定)
モデルにautosaveオプションをfalseで指定した場合の検証です。
app/models/customer.rb
class Customer < ActiveRecord::Base
has_one :address, dependent: :destroy, autosave: false
validates :name, presence: true
end
- 新規登録の場合: Customerオブジェクトのみデータベースに保存され、Addressオブジェクトはデータベース保存されない。
- 更新の場合: Customerオブジェクトの変更のみデータベースに保存され、Addressオブジェクトの変更はデータベースに保存されない。