Railsのリファクタリング記事です。
RailsでStrong Parametersを書くときに入力項目が多いページだと記述が多くなって「なんだかなぁ…」と思ったので、スッキリさせてみました。
テーブルからカラム名を引っ張ってゴニョゴニョして出力しています。
結論をすぐに知りたい方は後半のStrong Parametersをスッキリさせてみたへどうぞ。
Strong Parametersってなんだっけ?
Strong Parametersを使ってない人はいないと信じたいのですが、確認も含めて簡単に説明しておきます。
Strong ParametersはRails4系から追加されたセキュリティを向上させるための仕組みです。
こちらで指定したパラメータ以外は受け取らないようにして、攻撃者による意図しないコードの実行を防止するセキュリティ対策。
実際にどうやって使うのか。
例えば、送信された値からユーザーを作成するようなケースでは以下のように利用されます。
class UsersController < ApplicationController
def create
user = User.new(user_params)
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
これが基本系で、送信されてくるパラメータに:user
というキーが必須です。
:user
はハッシュ型の値を持っていて、このハッシュには:name
と:email
のキーのみを許可するという設定になりました。
モデルによって記述量が増える問題
通常、上記のような使い方をしますが、テーブルのカラム数が多くなると記述する値が増えてしまいます。
そんなにたくさんの項目を「1つのStrong Parametersに持たせる設計」にするべきかは別の問題としてありますが…
それは一旦置いておくとして、カラム分を追加していくと以下のような感じになると思います。
def user_params
params.require(:user).permit(:screen_name, :name, :kana, :nickname, :tel, :email, :address, :building, :gender, :birth_day)
end
このように、たくさん書かないといけません。面倒。
一行で記述すると読みづらいので、僕は今まで下記のように書いていました。
def user_params
params.require(:user).permit(
:screen_name, :name, :kana, :nickname,
:tel, :email, :address, :building,
:gender, :birth_day,
)
end
1つのアプリケーションで入力があるモデルは複数になることが多いと思います。
モデルが増えていくと、同様にモデルごとに大量のStrong Parametersを書くことになってしまい非常に手間です。
今回はこれを便利な感じにリファクタリングしてスッキリさせてみました。
Strong Parametersをスッキリさせてみた
上記のようにStrong Parametersに記述する値はモデルのテーブル名と一致することが多いと思います。(一致していないとRailに乗っていないような…)
これを利用して、モデルのテーブル名からcolumn_names
を使って一括で出力します。
column_names
はActiveRecord::Baseに用意されているメソッドです。
column_names
で出力されるのは文字列型の配列なので、どのモデルからでもシンボル型の配列で取得できる共通のクラスメソッドを用意しておきます。
ついでにリストから除外したいカラム名を配列で渡せるような引数(reject)も用意します。
module CommonModule
extend ActiveSupport::Concern
module ClassMethods
# カラム名のシンボル型の配列
def column_symbolized_names(reject=[])
column_names.delete_if {|n| n.in?(reject)}.map(&:to_sym)
end
end
end
このModuleを共通メソッドとしてモデルで読み込みます。
class User < ApplicationRecord
# 共通設定の読み込み
include CommonModule
end
あとは用意しておいたクラスメソッドでカラム名をそのままStrong Prametersに格納します。
def user_params
columns = User.column_symbolized_names
params.require(:user).permit(*columns)
end
(注意)
通常の方法であればupdated_at
とcreated_at
はStrong Parametersには含めなくてもデフォルト値を入れてくれるので、記述は不要なのですが、同様にrejectに含めてしまうと、保存時にMySQLからField 'created_at' doesn't have a default value
と怒られてしまいました。
Railsの仕様なのでしょうか…理由がよくわからなかったのですが、ご存知の方がいたら教えてください
ただし、この方法でもStrong Parametersに不要なカラムや、配列・連想配列を持ちたいパラメータには対応しないので、そこだけはカスタマイズします。
例えば、配列を保持するtagsパラメータが必要だとしたら…
def user_params
reject = %w(tags) # 出力する配列からimagesを除外しておく
columns = User.column_symbolized_names(reject).push(tags: [])
params.require(:user).permit(*columns) # 出力された配列にPush
end
tagsカラムを一度rejectして、最後にcolumnsに追加するだけ。
メタプログラミングを駆使すればもう少し共通化できそうな気もしますが、柔軟性も担保する場合はこのぐらいの共通化、簡略化が妥当かなと思います。
上記のように共通化することで、様々なケースに柔軟に対応可能でほぼほぼコピペで使いまわせちゃうStrong Parametersを作ることができました。