更新日:
【Rails】 permitメソッドを使ってストロングパラメーターにしよう
permitメソッドとは、paramsで取得したパラメーターに対し保存の許可処理を行うメソッドです。
1
2
3
4
5
6
7
8
9
#一次元ハッシュの場合
params.permit(:キー名)
# 二次元ハッシュの場合
params.require(:モデル名).permit(:キー名)
# 例
params.require(:user).permit(:name, :age)
=>{ name: "pikawaka", age: 25 }
paramsとはフォームなどによって送られてきた情報(パラメーター)を取得するメソッドです。
permitメソッドの使い方
この章では、permitメソッドの使い方について1つ1つ解説します。
どんなときに使うか
Rails4からcreateメソッドやupdateメソッドでレコードを保存するとき、フォームで送られてきたパラメーターをそのまま指定して保存や上書きをすることができなくなりました。
保存するときはこのパラメーターをストロングパラメーターにする必要があります。
そのときに使うメソッドがpermitメソッド
です。
ストロングパラメーターとは
permitメソッド
によって保存するパラメーターの許可処理を行ったパラメーターのことをストロングパラメーターといいます。
なぜわざわざストロングパラメーターにしないと保存できない仕組みになっているのでしょうか?
それはparamsで取得したパラメーターに悪意を持ったユーザーが保存したくないパラメーター属性を含めてしまうと、その情報まで保存されてしまう危険性があるためです。
例えば、下記のようなフォームで投稿したとします。
この時params
で下記のようなパラメータを取得することができます。
1
2
3
4
5
6
7
8
9
params
=> <ActionController::Parameters {
"utf8"=>"✓",
"authenticity_token"=>"トークンが入ります",
"user"=>{"name"=>"pikawaka", "age"=>"25", "tall"=>"170", "weight"=>"60"},
"commit"=>"送信",
"controller"=>"users",
"action"=>"create"
} permitted: false>
2重のハッシュ構造になっているのでrequireメソッド
を使い、キーがuserのバリューのみを取り出します。
1
2
3
4
5
6
7
params.require(:user)
=> <ActionController::Parameters {
"name"=>"pikawaka",
"age"=>"25",
"tall"=>"170",
"weight"=>"60"
} permitted: false>
ストロングパラメータについては、こちらのRuby on Railsの良書として有名な書籍でも触れられています!
requireメソッド
パラメーターの中にモデルに対応するキーが存在するかを確認し、存在する場合にそのバリューを返します。
上の例だとモデルに対応するキーはuser
になります。
このようにキーがuser
のバリューが取り出せたことが確認できます。
requireメソッド
を使わなくてもハッシュからバリューを取り出すような記述でも中身を取り出すことができます。
1
2
3
4
5
6
7
params[:user]
=> <ActionController::Parameters {
"name"=>"pikawaka",
"age"=>"25",
"tall"=>"170",
"weight"=>"60"
} permitted: false>
この時の返り値はuser.require(:user)
と全く同じになります。
上の例だと取り出したバリューの中にキーとバリューのセットが4つあります。
railsはマスアサインメント機能
というのが用意されており、複数の情報をまとめて指定することができます。
今回であればこの4つをまとめて一度に処理することができます。
rails3までは下記のように記述することでデータの保存が可能でした。
1
User.create(params[:user])
上のようにしてマスアサインメント機能を使い4つのパラメーターを一度に保存できます。
とても便利なように見えますが、フォームから送られてきたデータの中に悪意を持ったユーザーが保存したくないパラメーター属性を含めてしまうと、そのまま保存されてしまう危険性があります。
これはChormeの検証モードなどを使うと簡単にできてしまいます。
上の例では検証モードの「Edit as HTML」を使い、権限を入力できるフォームを追加し値を入力して送信しています。
この時どういうパラメーターが送られているか確認してみましょう。
1
2
3
4
5
6
7
8
9
# 保存したくないパラメーター「role」が含まれている
params.require(:user)
=> <ActionController::Parameters {
"name"=>"テスト",
"age"=>"25",
"tall"=>"170",
"weight"=>"65",
"role"=>"0"
} permitted: false>
上の例のようにパラメーターの中に"role"=>"0"
が追加されてしまいました。
もしusersテーブルにユーザーが管理人(0)か一般ユーザー(1)かを判断するroleカラムが存在すれば管理者で登録できるパラメーターとしてcreateメソッドに送られます。
この時rails3までの保存方法で保存ができてしまうと困りますね。
そのためRailsではRails4から保存の許可処理を経由しないパラメーターを無効にするStrongParameters
という仕組みができました。
実際の使い方
それでは実際にpermitメソッド
を使ってストロングパラメーターにしてみましょう。
先程のフォームを例にとってみます。
ここで注目するのは最後のpermitted: false
という部分です。
これがfalseだとマスアサインメント機能が使われるのを防ぐ仕組みになっています。
この時下記のコードで保存しようとするとエラーが発生します。
1
User.create(params.require(:user))
下記のようなエラーが発生し、保存ができません。
ではpermitメソッドを使ってみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
params
=> <ActionController::Parameters {
"utf8"=>"✓",
"authenticity_token"=>"トークンが入ります",
"user"=>{"name"=>"pikawaka", "age"=>"25", "tall"=>"170", "weight"=>"60"},
"commit"=>"送信",
"controller"=>"users",
"action"=>"create"
} permitted: false>
# requireメソッドでuserのバリューを取り出す
params.require(:user)
=> <ActionController::Parameters {
"name"=>"pikawaka",
"age"=>"25",
"tall"=>"170",
"weight"=>"60"
} permitted: false>
#permitメソッドでストロングパラメーターにする
params.require(:user).permit(:name, :age, :tall, :weight)
=> <ActionController::Parameters {
"name"=>"pikawaka",
"age"=>"25",
"tall"=>"170",
"weight"=>"60"
} permitted: true>
返ってくるパラメーターは全く同じですが、最後のpermittedがtrueに変わっているのが確認できます。
この部分がtrueになってはじめてマスアサイメント機能が使え保存や上書きができるようになります。
このように無事保存されました。
もし先程のroleといった意図しないキーがparamsに入っていてもpermitメソッドで保存を許可していないので保存される心配はありません。
paramsでは取得できてしまったroleカラムには保存されていないのが確認できます。
配列に対しての指定の仕方
バリューに複数の値が入るときは配列となってパラメーターに入ります。
その際、下記のようにpermitメソッド
を使います。
1
params.permit(キー: [])
例えばform_forやform_withでフォームを作成する際、collection_check_boxes
で複数の値を保存するときなどに使います。
例としてユーザーは複数のグループに所属でき、グループはたくさんのユーザーを持つことができる多対多の関係であるとします。
複数のチェックボックスを作成し、ユーザーを複数選択できるフォームを作成しました。
1
<%= f.collection_check_boxes :user_ids, User.all, :id, :name %>
下記のようなフォームが作成されるので複数をチェックをして投稿してみます。
この時のparamsの構造をみてみましょう。
1
2
params
=> "group"=>{"name"=>"ピカわか", "user_ids"=>["", "1", "5", "7"]}
このようにuser_idsのバリューが配列となっているためpermitメソッド
は下記のように記述します。
※{ user_ids: [] }
の部分がcollection_check_boxesの値を指しています。
1
2
3
def user_params
params.require(:group).permit(:name, { user_ids: [] })
end
多対多の関係のため保存が成功すると中間テーブルにもレコードが作成されます。
ハッシュパラメーターの指定の仕方
requireメソッド
を使うときのようなハッシュの中にハッシュがあるようなパラメーターのときにはrequireメソッド
を使わず、下記のようにハッシュ全体に対してpermitメソッド
を使うことができます。
1
params.permit(モデル名: {})
実際の例をみてみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
params
=> <ActionController::Parameters {
"utf8"=>"✓",
"authenticity_token"=>"トークンが入ります",
"user"=>{"name"=>"pikawaka", "age"=>"25", "tall"=>"170", "weight"=>"60"},
"commit"=>"送信",
"controller"=>"users",
"action"=>"create"
} permitted: false>
params.permit(user: {})
Unpermitted parameters: :utf8, :authenticity_token, :commit
=> <ActionController::Parameters {
"user"=><ActionController::Parameters {
"name"=>"pikawaka",
"age"=>"25",
"tall"=>"170",
"weight"=>"60"
} permitted: true>
} permitted: true>
上のUnpermitted parameters: :utf8, :authenticity_token, :commit
は許可していないパラメーターを指します。
このままでは保存できないので、userのバリューを取り出します。
1
2
3
4
5
6
7
8
params.permit(user: {})[:user]
Unpermitted parameters: :utf8, :authenticity_token, :commit
=> <ActionController::Parameters {"name"=>"pikawaka", "age"=>"25", "height"=>"170", "weight"=>"60"} permitted: true>
# requireメソッドでも可能
params.permit(user: {}).require(:user)
Unpermitted parameters: :utf8, :authenticity_token, :commit
=> <ActionController::Parameters {"name"=>"pikawaka", "age"=>"25", "height"=>"170", "weight"=>"60"} permitted: true>
このように保存することができました。
ただこの時は下記の2点に注意しましょう。
1.許可するカラムを指定しないので全てのカラムが許可されてしまう
2.後から追加したカラムも許可されてしまう
permit!メソッド
permit!メソッド
を使うと全てのキーを許可することができます。
1
2
3
4
5
6
7
params.require(:user).permit!
=> <ActionController::Parameters {
"name"=>"pikawaka",
"age"=>"25",
"height"=>"170",
"weight"=>"60"
} permitted: true>
上のコードの結果はparams.require(:user).permit(:name, :age, :height, :weight)
と一緒になります。
保存ができるかどうか確認してみましょう。
このようにしっかり保存ができました。
permit!メソッド
を使うときは下記の2点に注意しましょう。
1.許可するカラムを指定しないので全てのカラムが許可されてしまう
2.後から追加したカラムも許可されてしまう
例えば先程の例のようにchromeの検証機能を使ってこちらが意図しないパラメーターが送られてしまったとします。
この時のparamsの中身は下記のようになっています。
このようにroleが0というパラメーターも含まれてしまいました。
この時にpermit!メソッド
を使ってストロングパラメーターにして保存するとどうなってしまうでしょうか?
このように保存されてはいけないroleカラムにもデータが保存されてしまいました。
ですのでpermit!メソッド
でストロングパラメーターにするのはおすすめしません。
ネストしているパラメーターに対しての使い方
下記のようにパラメーターがネストしている場合はどのように指定したらよいでしょうか?
1
2
3
4
5
6
7
8
params
=> <ActionController::Parameters {
"name"=>"pikawaka",
"address":=>{
"prefecture"=> "Toyo",
"city":=>"Shibuya"
}
} permitted: false>
この場合は下記のように記述します。
1
params.permit(:name, address: [:prefecture, :city])
この時の返り値は下記のようになります。
1
2
3
4
5
Unpermitted parameters: :utf8, :authenticity_token, :commit
=> <ActionController::Parameters {
"name"=>"pikawaka",
"address"=><ActionController::Parameters {"prefecture"=>"Tokyo", "city"=>"Shibuya"} permitted: true>
} permitted: true>
配列が1回ネストしている時
下記のように配列が1回ネストしているパラメーターがあります。
1
2
3
4
5
6
7
8
9
params
=> <ActionController::Parameters {
"name":=>"pikawaka",
"friends":=>[{
"name"=>"miyajima"
},{
"name"=>"suzuki"
}]
} permitted: false>
この場合は下記のように記述します。
1
params.permit(:name, friends: [:name])
この時の返り値は下記のようになります。
1
2
3
4
5
6
7
8
9
Unpermitted parameters: :utf8, :authenticity_token, :commit
=> <ActionController::Parameters {
"name"=>"pikawaka",
"friends"=>[<ActionController::Parameters {
"name"=>"miyajima"
} permitted: true>, <ActionController::Parameters {
"name"=>"suzuki"
} permitted: true>]
} permitted: true>
配列が2回ネストしている時
下記のように配列が2回ネストしているパラメーターがあります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
params
=> <ActionController::Parameters {
"name"=>"pikawaka",
"friends"=>[{
"name"=>"miyajima",
"address"=>{
"prefecture"=>"Tokyo",
"city"=>"Meguro",
},
"friends"=>[{
"name"=>"tanaka"
}]},{
"name"=>"suzuki",
"address"=>{
"prefecture"=>"Saitama",
"city"=>"Kawagoe",
},
"friends"=>[{
"name"=>"satou"
}]
}]
} permitted: false>
この場合は下記のように記述します。
1
params.permit(:name, { friends: [:name, { address: [:prefecture, :city], friends: [:name] }] })
この時の返り値は下記のようになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Unpermitted parameters: :utf8, :authenticity_token, :commit
=> <ActionController::Parameters {
"name"=>"pikawaka",
"friends"=>[<ActionController::Parameters {
"name"=>"miyajima",
"address"=><ActionController::Parameters {
"prefecture"=>"Tokyo",
"city"=>"Meguro"
} permitted: true>, "
friends"=>[<ActionController::Parameters {
"name"=>"tanaka"
} permitted: true>]} permitted: true>, <ActionController::Parameters {
"name"=>"suzuki",
"address"=><ActionController::Parameters {
"prefecture"=>"Saitma",
"city"=>"Kawagoe"
} permitted: true>,
"friends"=>[<ActionController::Parameters {
"name"=>"satou"
} permitted: true>]} permitted: true>]} permitted: true>
この記事のまとめ
- permitメソッドは、利用可能なパラメーターを指定するメソッドのこと
- ストロングパラメーターを作成するときに使う
- Rails4からは、ストロングパラメーターにしないと保存や上書きが出来ないので注意が必要!