#目次
1.背景
2.Strong Parametersについておさらい
3.モデルに、accepts_nested_attributes_forを使っている場合
4.参考
##1.背景
Railsでオリジナルアプリを開発しているが、「Unpermitted parameter: :_destroy」という表示が出てしまい、それをRailsガイドを確認しながら解決させたので、その学びをアウトプットするため、記載する。
##2.Strong Parametersについておさらい
Railsガイドの説明によると、Strong Parametersとは、
strong parametersを用いることで、Action Controllerのパラメータが許可されるまでActive Modelの「マスアサインメント」に利用されることを禁止できます。つまり、多くの属性を一度に更新したい場合は、どの属性のマスアップデートを許可するかを開発者が明示的に指定しなければなりません。大雑把にすべての属性の更新を一括で許可してしまうと、外部に公開する必要のない属性まで誤って公開してしまう可能性が生じるため、そのような事態を防ぐための機能です。
さらに、パラメータの属性に「必須 (required)」を指定することで、事前に定義したraise/rescueフローによって、渡された必須パラメータが不足している場合に「400 Bad Request」で終了させることもできます。
そして、Controllerの例について、こちらもRailsガイドの例を参照。
class PeopleController < ActionController::Base
# 以下のコードはActiveModel::ForbiddenAttributesError例外を発生します
# 明示的な許可を行なわずに、パラメータを一括で渡してしまう
# 危険な「マスアサインメント」が行われているからです。
def create
Person.create(params[:person])
end
# 以下のコードは、パラメータにpersonキーがあれば成功します。
# personキーがない場合は
# ActionController::ParameterMissing例外を発生します。
# この例外はActionController::Baseにキャッチされ、
# 400 Bad Requestを返します。
def update
person = current_account.people.find(params[:id])
person.update!(person_params)
redirect_to person
end
private
# 許可するパラメータはprivateメソッドでカプセル化します。
# これは非常によい手法であり、createとupdateの両方で使いまわすことで
# 同じ許可を与えることができます。また、許可する属性をユーザーごとにチェックするよう
# このメソッドを特殊化することもできます。
def person_params
params.require(:person).permit(:name, :age)
end
end
##3.モデルに、accepts_nested_attributes_forを使っている場合
accepts_nested_attributes_for(クラスメソッド)を使っている場合、下記のように記述することで、Relation関係にあるレコードの更新・削除が可能。このメソッドは、id
と_destroy
パラメータに基づいて動作する。
<Railsガイドの事例>
params.permit(:name, { emails: [] },
friends: [ :name,
{ family: [ :name ], hobbies: [] }])
<私のオリジナルアプリ開発の事例>
[省略]
def create
@record = Record.new(record_params)
if @record.save
flash[:success] = "練習内容の登録が完了しました。"
redirect_to records_url
else
flash[:alert] = "登録に失敗しました。"
render :new
end
end
[省略]
def record_params
params.require(:record).permit(:record_id, :training_date, :learning_point, outputs_attributes:[:output_name, :id, :_destroy], practices_attributes:[:practice_item, :practice_time, :id, :_destroy], tasks_attributes:[:task_name, :id, :_destroy]).merge(user_id: current_user.id)
end
子テーブルにある属性を、
params.require(:model).permit(:models_attributes:[:id,:arg, :arg2, :_destroy]
の形でまとめると実装できる。
※モデルに「has_many」「belongs_to」を忘れないこと
##4.参考