开发者

What is the most elegant way to validate the presence of ONLY one out of two attributes using Rails?

开发者 https://www.devze.com 2022-12-24 04:31 出处:网络
class Followup < ActiveRecord::Base belongs_to :post belongs_to :comment end This model needs to only have either a post or a comment, but only one of the two.
class Followup < ActiveRecord::Base
  belongs_to :post
  belongs_to :comment
end

This model needs to only have either a post or a comment, but only one of the two.

Here's the rspec for what I'm trying to do:

  it "should be impossible to have both a comment and a post" do
    followup = Followup.make
    followup.comment = Comment.make
    followup.should be_valid
    followup.post = Post.make
    f开发者_StackOverflow中文版ollowup.should_not be_valid
  end

I can see a bunch of solution to do this, but what would be the most elegant way of doing this?


I think what you really want is a polymorphic association.

Ryan does a great job explaining them in Railscast #154.

class Followup < ActiveRecord::Base
  belongs_to :followupable, :polymorphic => true
end

class Post < ActiveRecord::Base
  has_many :followups, :as => :followupable
end

class Comment < ActiveRecord::Base
  has_many :followups, :as => :followupable
end


The question of elegance is of course subjective but the following example will do exactly what you want, including separate error messages for the 2 invalid conditions i.e both values provided and no values provided.

class Foo < ActiveRecord::Base  
  validate :one_and_only_one  

  def one_and_only_one()  
    errors.add_to_base("You must provide either a foo or a bar")  
      if self.foo.blank? && self.bar.blank?  
    errors.add_to_base("You cannot provide both a foo and a bar")  
      if !self.foo.blank? && !self.bar.blank?  
  end  
end  

EDIT

Thinking of more solutions then this might pass the elegance test better

errors.add_to_base("You must provide either a foo or a bar") 
  unless [f.foo, f.bar].compact.length == 1

Although this will fail on space filled fields.

0

精彩评论

暂无评论...
验证码 换一张
取 消