ActiveRecordでid以外の主キーを指定すると、createメソッドが使えない

主キーを例えば「code」にしたいという場合、
「set_primary_key」メソッドを使って明示的に主キーを指定するのだが、
この「code」が連番などでない自然キーだったりすると、
当然フォームからキーを入力したくなる。





ところが


User.create(code: 'a', name: 'Tom')


などとしても「code」には値が設定されず、warningが出てきてしまう。


WARNING: Can't mass-assign these protected attributes: code





idは通常連番なので、外部から明示的に指定できないようにしているようなのだが、
主キーが自然キーで手動入力したい場合は困ることになる。



↓ダメ
user = User.create(params[:user])


↓これもダメ
user = User.new(params[:user])


↓これは大丈夫
user = User.new
user.code = 'a'
user.save





http://guides.rubyonrails.org/security.html#mass-assignment
↑この仕組みで保護されているので、これを回避するには、





1.attr_accessor(:code, :name)などと明示的に保護しないすべてのカラムを指定する(カラムが多いと面倒)


2.海外のブログによると「'composite_primary_keys」gemを使うといけるとあったが、今日やってみたらダメだった
http://roninonrails.blogspot.com/2008/06/using-non-standard-primary-keys-with.html


3.user = User.create(params[:user], without_protection: true)
こんな感じで明示的に保護機能を無効にする


4.モデルで以下のように保護カラム一覧から該当のカラムを削除する
protected_attributes.delete('code')





今回は「4」のやり方で回避した。「1」はカラムが多くなると面倒だし、指定し忘れもあり得る。「2」はできなかったため。「3」はcodeに対する保護機能は無効にしたいが、保護機能自体は使いたいという場合に使えないため、というのが理由だ。





protected_attributesはBlackListクラスのオブジェクトなのだが、
BlackListクラスはさかのぼるとRubyの標準添付ライブラリ「Set」を継承しているので、
Setクラスのdeleteメソッドを使った。





ちなみに「type」カラムもこの保護が利いているが、
typeの場合は前にも書いたように回避方法がある。
http://d.hatena.ne.jp/twotrees/201106





今回の対処は実装を追った場当たり的な対処なので、
実装が変更になると動かなくなるのが難点だが、
一応今回の目的には一番合致したので採用した。
また使うかもしれないので備忘録として残しておく。