追記:inverse_ofを使いましょう。
http://319ring.net/blog/archives/2724
以下、原文。
Railsやってていつもbelongs_toの項目の検証をどうするのかで迷っていたのですが、自分はこれが最適解なのかなーと思ったのがあったので書いておきます。
belongs_toのデータを必須にしたいのですが、:presence => trueだと必須というだけでちゃんとリレーション先に存在するidじゃない場合があるので信頼性にかけるので、どうするべきか?と悩んでいました。:inclusionでやるのがいいのかな?と思っていたのですが、どうにも重たそうだし、ほかに方法があるんじゃないのかなーと。
Web+DB Pressの70号で紹介されているのですが、Rails3.2以降ならば、modelでpluckというメソッドが使えるようになっています。これは、指定したカラムの配列を返すメソッドです。モデルのオブジェクトが生成されることがないため、高速で動作します。
User.pluck(:id) #=> [1,2,3,4,5] みたいな感じ
これを使って、belongs_toで関連している項目の検証を行ってみました。
# coding: utf-8 class User < ActiveRecord::Base attr_accessble :name, :role, :role_id belongs_to :role, :readonly => true validates :role_id, :inclusion => {:in => Role.pluck(:id)} validates :name, :presence => true end
これを検証してみるために、RSpecを書いてみます(FactoryGirlでfixture作ってます)。
# coding: utf-8 require 'spec_helper' describe User do let(:role) {FactoryGirl.create(:role)} describe "正しいデータを入れた場合" do subject {FactoryGirl.build(:user, :role => role)} it("エラーがないこと") {should be_valid} end end
一見、通りそうなのですが、エラーになりました…。
原因がわからなかったのでググってみたところ、:inclusion => {:in => Role.pluck(:id)}の、Role.pluck(:id)が評価されるタイミングがずれるため、エラーになっているようでした。ここを以下のようにprocで評価するように書き換えたら、ちゃんとテストが通りました。
# coding: utf-8 class User < ActiveRecord::Base attr_accessble :name, :role, :role_id belongs_to :role, :readonly => true validates :role_id, :inclusion => {:in => proc{Role.pluck(:id)}} validates :name, :presence => true end
ここまで書いて、普通にRole.exists?(role_id)で評価すればよくね?と気づいた…。
なんでinclusionで比較しようとしていたんだ、俺は…。
最終的にこう直した。
# coding: utf-8 class User < ActiveRecord::Base attr_accessble :name, :role, :role_id belongs_to :role, :readonly => true validates :name, :presence => true validate :role_exists? private def role_exists? unless Role.exists?(role_id) errors.add(:role_id, I18n('activerecord.errors.messages.inclusion')) end end end
たぶんこれが最適解なんだろうなー…。もっといい方法があるよ!という方がいらっしゃいましたら教えてください。
これくらいであれば、presenceだけでもできますよ.
class User < ActiveRecord::Base
belongs_to :role
validates :role, presence: true
end
コメントありがとうございます。
これ書いた時はだいぶ前なんですが、inverse_ofを使うのが最適解でした。
http://319ring.net/blog/archives/2724