validates_uniqueness_ofのscopeオプションを使う際の注意

scopeオプション

RailsActiveRecord::Base.validates_uniqueness_ofは次のような使い方ができる。

class Comment
  validates_uniqueness_of :blog_id, :scope => :user_id  


これは、commentsデーブルに、同一のuser_idに対するblog_idは一意である。という制約を持たせている。

scopeオプションを使用する際の注意

この場合、以下のようなコードを書いては行けない。

class Blog < ActiveRecord::Base
  has_many :comments

  def add_comment(comment, commenter)
    comment = Comment.new(:content => comment, :user_id => commenter.id)
    
    if comment.valid? 
      self.comments << comment
    end
  end
end


まずいのは、valid?のタイミング。この時は、commentオブジェクトは、どのblogと関連付けられるのかを知らないので、userは同一のblogに対しては1つしかコメントできないという制約の検証が正常に行われない。
この場合は、「self.comments << comment」がfalseになる。valid?した後にfalseというのはあまり良くない。
よって、次のように書きます。

def add_comment(comment, commenter)
  comment = Comment.new(:content => comment, 
                        :user_id => commenter.id,
                        :blog_id => self.id)
  
  if comment.valid? 
    self.comments << comment
  end
end

これによって、valid?を呼ぶ際に、commentオブジェクトは、user_idとblog_idの両方を知っているので、validates_uniqueness_ofの検証が正常に行われる。