Simple minds think alike

より多くの可能性を

【Rails】ビューに date_select や date_time_select を使ったフォームで UnknownAttributeError が発生する場合の対処

利用ケースは少ないと思いますが、ActiveRecord::Base を継承しないフォームと date_selectdate_time_select を使ったビューの組み合わせだと、フォーム初期化時に UnknownAttributeError が発生していて、原因と対処方法がよく分からず若干ハマってしまったので誰かのためにメモで書いておきます。

前提

UnknownAttributeErrorが発生する原因

以下のようにdate_selectdate_time_select を使ってビューを実装します。

- # app/views/plans/new.html.haml
= form_for(@plan) do |f|
  .field
    = f.date_select :start_date
    = f.date_select :end_date

【見た目】

するとコントローラ側の変数paramsには、start_date(1i), start_date(2i), start_date(3i) のように入ってきます。そして、コントローラ側のStrongParameterの機能 (以下のplan_paramsの箇所)までは、これらの値はstart_dateと解釈してくれてうまく動きます。

# app/controllers/plan_controller.rb
class PlanController < ApplicationController
  def new
   @plan_form =  PlanForm.new(plan_params)
  end

  private

  def plan_params
    params.require(:plan).permit(:start_date, :end_date)
  end
end

その後に、以下のようなフォームを作って同じコントローラでPlanForm.new(plan_params)のようにPlanFormクラスを初期化しようとすると

# app/forms/application_form.rb
class ApplicationForm
  include ActiveModel::Model
  include ActiveModel::Attributes
end

# app/forms/plan_form.rb
class PlanForm < ApplicationForm
  attribute :start_date
  attribute :end_date
end

フォームPlanFormstart_date(1i)、start_date(2i)、start_date(3i) という属性がないため(start_dateならある)、UnknownAttributeError が発生します。

ちなみにフォームではなく、データベースに値を保存するActiveRecord::Baseを継承したモデルでは、assign_multiparameter_attributesというメソッドの中でうまく処理してくれます。

対処方法

こういう時はフォームに include ActiveRecord::AttributeAssignmentを追加すると start_date(1i)、start_date(2i)、start_date(3i)start_date と解釈してくれるようになって、フォーム(PlanForm)を初期化できるようになります。

# app/forms/application_form.rb
class ApplicationForm
  include ActiveModel::Model
  include ActiveModel::Attributes
  include ActiveRecord::AttributeAssignment # ←これを追加
end

# app/forms/plan_form.rb
class PlanForm < ApplicationForm
  attribute :start_date
  attribute :end_date
end

参考資料