すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Rails

【Rails】 permitメソッドを使ってストロングパラメーターにしよう

ぴっかちゃん
ぴっかちゃん

permitメソッドとは、paramsで取得したパラメーターに対し保存の許可処理を行うメソッドです。

コントローラー | 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で下記のようなパラメータを取得することができます。

コンソール | 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のバリューのみを取り出します。

コンソール | requireメソッドの実行
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になります。

requireメソッド

このようにキーが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までは下記のように記述することでデータの保存が可能でした。

コントローラー | 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

ここで注目するのは最後のpermitted: falseという部分です。
これがfalseだとマスアサインメント機能が使われるのを防ぐ仕組みになっています。
この時下記のコードで保存しようとするとエラーが発生します。

コントローラー | エラーで保存不可 -->
1
User.create(params.require(:user))

下記のようなエラーが発生し、保存ができません。

エラーが発生し保存されない

ではpermitメソッドを使ってみましょう。

コンソール | 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メソッドで保存を許可していないので保存される心配はありません。

roleは保存されない

paramsでは取得できてしまったroleカラムには保存されていないのが確認できます。

配列に対しての指定の仕方

バリューに複数の値が入るときは配列となってパラメーターに入ります。
その際、下記のようにpermitメソッドを使います。

コントローラー | 配列の指定 -->
1
params.permit(キー: [])

例えばform_forform_withでフォームを作成する際、collection_check_boxesで複数の値を保存するときなどに使います。

例としてユーザーは複数のグループに所属でき、グループはたくさんのユーザーを持つことができる多対多の関係であるとします。
複数のチェックボックスを作成し、ユーザーを複数選択できるフォームを作成しました。

ビューファイル | 複数のチェックボックスを作成
1
<%= f.collection_check_boxes :user_ids, User.all, :id, :name %>

下記のようなフォームが作成されるので複数をチェックをして投稿してみます。

投稿する

この時のparamsの構造をみてみましょう。

コンソール | 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は許可していないパラメーターを指します。

params.permit(user: {})

このままでは保存できないので、userのバリューを取り出します。

コンソール | 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!メソッドを使うと全てのキーを許可することができます。

コンソール | 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)と一緒になります。

どちらも等しい

保存ができるかどうか確認してみましょう。

params.require(:user).permit!

このようにしっかり保存ができました。

permit!メソッドを使うときは下記の2点に注意しましょう。
1.許可するカラムを指定しないので全てのカラムが許可されてしまう
2.後から追加したカラムも許可されてしまう

例えば先程の例のようにchromeの検証機能を使ってこちらが意図しないパラメーターが送られてしまったとします。

意図しないパラメーター

この時のparamsの中身は下記のようになっています。

paramsの中身

このようにroleが0というパラメーターも含まれてしまいました。
この時にpermit!メソッドを使ってストロングパラメーターにして保存するとどうなってしまうでしょうか?

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回ネスト -->
1
2
3
4
5
6
7
8
9
params
=> <ActionController::Parameters {
  "name":=>"pikawaka",
  "friends":=>[{
      "name"=>"miyajima"
    },{
      "name"=>"suzuki"
    }]
} permitted: false>

この場合は下記のように記述します。

コントローラー | 配列が1回ネストしている時の記述 -->
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回ネストしているパラメーターがあります。

コンソール | 配列が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>

この場合は下記のように記述します。

コントローラー | 配列が2回ネストしている時の記述 -->
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からは、ストロングパラメーターにしないと保存や上書きが出来ないので注意が必要!