What is the most elegant way to validate the presence of ONLY one out of two attributes using Rails?
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.
精彩评论