概要
Strong Parameters 対応でattr_accessible 指定が排除された環境において、
仮想属性を含んだJSON データがPOST された際に、params のネスト部分にも仮想属性を含める方法について。
※仮想属性・・・DBにカラムを持たない仮想的な属性。確認用パスワードなどがその例。
事の発端
Strong Parameters 対応で、Post モデルからattr_accessible 指定を排除した。
class Post < ActiveRecord::Base
acts_as_paranoid
- attr_accessible :created_at, :deleted_at, :user_id
- attr_accessible :image_id, :note
- attr_accessible :status
- attr_accessible :version
このとき、Api::PostsController に渡るはずのパラメータが不足していた。
送信したJSON:
{
"post": {
"image_id": "1",
"note": "test note"
}
}
期待したparams:
{"image_id"=>"1", "note"=>"test note", "post"=>{"image_id"=>"1", "note"=>"test note"}}
実際に渡ってきたparams:
{"image_id"=>"1", "note"=>"test note", "post"=>{}}
※image_id、note は共にPostモデルの仮想属性
解決策
Controller に以下の要領で、JSON のルートノードとネストパラメータに含めたい仮想属性名を記載する。
wrap_parameters :root_node_name, include: [:hash_key_1, :hash_key_2, …]
上の例であれば、次のように記載する。
class Api::PostsController < Api::ApplicationController
wrap_parameters :post, include: [:image_id, :note]
理由
JSON データの場合、データを扱いやすくするようにRails がparams の加工を行っており、ネストされたパラメータの部分については次のように格納するパラメータを決定している。
- 明示的にオプション(attr_accessible、ParamsWrapper の:include や:exclude など)がある場合はそれに従ってハッシュを作る
- そうでない無指定の場合は各モデルのクラスメソッドattribute_names を叩くと返ってくるテーブルのカラム名に基づいてハッシュを作る
仮想属性でなければ、特にオプション指定をせずともattribute_names でカラム名が返るので下記のようにネストパラメータが生成される。
JSON データ: {"user": {"name": "xx", "age": "yy"}}
↓
controller に渡るデータ: {"name"=>"xx", "age"=>"yy", :user => {"name"=>"xx", "age"=>"yy"}}
しかし、仮想属性の場合は、attribute_names を叩いてもそれら属性名が返ることは無いので、オプション未指定ではネストパラメータに含める候補からから抜け落ちてしまっていた。
オプションの指定方法は上述「結論」の通り。