データに履歴を残したい
データの履歴はとっても大事だなと感じ始めた昨今、履歴を残しつつ、しかし普段は最新のデータのみ参照したい。
そして、データの更新時は既存のデータを更新ではなく、新しい履歴を作成したい。
やってみる
Modelの設定
モデルにFoo
とHistory
があって、1対多の関係であるとする。
Foo
には、nameという属性があるとする。
History
には、memoという属性があり、それの履歴が保存されていくとする。
fields_forで更新したいため、accept_nested_attributes_for
を使う。ミソなのは、limit: 1
にしておくことだ。
class Foo < ApplicationRecord
has_many :histories, inverse_of: :foo
has_one :latest_history, -> { order(created_at: :desc) }, class_name: :History
accepts_nested_attributes_for :histories, limit: 1
validates :name, presence: true
end
class History < ApplicationRecord
belongs_to :foo
validates :foo, presence: true
validates :memo, presence: true
end
Viewの設定
Viewでは、fields_for
を使って表現する。1件だけを対象にしたいので、@foo.histories.last
を使う。
なお、Viewはslimで記述、simple_formを使っている前提で書く。
= simple_form_for @foo do |f|
= f.input :name
= f.simple_fields_for :histories, @foo.histories.last do |fh|
= fh.input :memo
= f.button :submit
Controllerの設定
登録の場合
Controller側では、actionがnewの場合は、@foo.histories.build
するだけでよい。
class FoosController < ApplicationController
# ~ 略
def new
@foo = Foo.new
@foo.histories.build
end
def create
@foo = Foo.new(foo_params)
if @foo.save
redirect_to @foo, notice: '成功'
else
flash.now[:alert] = '失敗'
render :new
end
end
# ~ 略
private
def foo_params
params.require(:foo).permit(
:name,
histories_attributes: [:memo]
)
end
end
更新の場合
問題はeditの時である。単純に@foo.histories.build
するとmemoが空になる。
editの場合は、latest_history
からデータを取ってきて、それを使ってbuildする。
class FoosController < ApplicationController
before_action :set_foo, only: [:show, :edit, :update, :destroy]
# ~ 略
def edit
@foo.histories.build(
# 自動で値が入るカラムを除外してbuildのパラメータとして利用する
@foo.latest_history.attributes.except('id', 'created_at', 'updated_at')
)
end
def update
if @foo.update(foo_params)
redirect_to @foo, notice: '成功'
else
flash.now[:alert] = '失敗'
render :edit
end
end
# ~ 略
private
def set_foo
@foo = Foo.find params[:id]
end
def foo_params
params.require(:foo).permit(
:name,
histories_attributes: [:memo]
)
end
end
これで、普段は最新の情報のみが閲覧できるが、フォームを開いたらメモの履歴は取れる状態となった。
学び
-
fields_for
の引数にオブジェクトを渡すことで、数を制限できる -
accept_nested_attributes
には件数制限のできるlimit
オプションがある -
ActiveSupport
にあるHashの指定キーを取り除くexcept
メソッドが便利