Rails validations presence of either one of two fields (XOR) - ruby-on-rails

How can I validate that either one of these values is present, but not both?
validates_presence_of :client_id, message: 'Please enter a value'
validates_presence_of :agency_id, message: 'Please enter a value'
I looked on the rails guides and I think I need to use conditional validations, but I'm still a little stuck.

Try this
validates :client_id, presence: true, unless: :agency_id
validates :agency_id, presence: true, unless: :client_id
If you want to include the error message, you can do
validates :client_id, presence: { message: "Must have a value" }, unless: :agency_id
You can read more about validation messages

If you use the unless syntax, you will get 2 errors: one when client_id and one when agency_id if both are Nil.
You would need a custom method if you want only one error. Guides: ActiveRecord Validation
validate :client_or_agency
def client_or_agency
errors.add(:client_id, "Either Client or Agency needs a value") unless client_id.present? || agency_id.present?
end

Related

rails validations allow_blank and presence

I am reading the book 'agile web development with rails' and I am at the part where they are going through validations, listed below:
class Product < ActiveRecord::Base
validates :description, :title, :image_url, presence: true
validates :price, numericality: {greater_than_or_equal_to: 0.01}
validates :title, uniqueness: true
validates :image_url, allow_blank: true, format: {
with: %r{\.(gif|jpg|png)\z}i,
message: 'Must include an image file extension'
}
end
Something I am not understanding is that we have image_url, allow_blank set to true, but then we have to verify that the image_url is present? This seems like a contradiction to me at first glance, but I'm sure it's from lack of understanding.
What is it that the allow_blank validation is doing? And why do we not validate the :price be present also?
I can see why you are confused about this---it is not very clear! The meaning of allow_blank: true is that if image_url is blank, then the format validator will not run. The presence validator will still run, because its declaration has no allow_blank option.
The reason the book is doing it this way is to avoid showing users 2 validation messages if they leave the field blank. You don't really want users to see "Image Url can't be blank; Image Url must include an image file extension". It's better to just show a message about it being blank. In other words, we only want to run the format validator if there is something to validate.

Rails Validation with if Condition

I just found the following line of code in a project I'm working on at my new employer:
validates :message, presence: true, if: :message
Am I missing something, or is this pointless?
It seems that it's validating the presence of the message, but only in the case that the message is set.
Validates is uses for Model level validation.
validates :message, presence: true
means you have to insert atleast one character.
If you are entering message on view then it is pointless to use if condition.
i assume this was used by someone who did not know about the allow_blank: true option. but this is a case for when you should use a comment on your code...

Rails validations if condition

I need your help validating a simple Rails model.
First i want to check if the user filled all input-fields:
class Test < ActiveRecord::Base
validates :firstname, :lastname, :firstvalue, :secondvalue, presence: true
[...]
I also want to check if the :secondvalue param is bigger than :firstvalue.
Adding
validate :biggerThan
def biggerThan
if self.secondvalue < self.firstvalue
errors.add(:secondvalue, "must be bigger than First")
end
end
Well this works, but only if all other fields are filled! Creating a new Entry leaving all fields blank i am getting an undefined method <' for nil:NilClass.
Could you help me?
You can do this
validate :biggerThan, if: Proc.new{ |test| test.firstvalue.present? and test.secondvalue.present? }
It would be good if you add numericality validations also
validates :firstvalue, numericality: true
validates :secondvalue, numericality: true

Rails validate uniqueness only if conditional

I have a Question class:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id
end
A given user can only create a single question per day, so I want to force uniqueness in the database via a unique index and the Question class via validates_uniqueness_of.
The trouble I'm running into is that I only want that constraint for non-admin users. So admins can create as many questions per day as they want. Any ideas for how to achieve that elegantly?
You can make a validation conditional by passing either a simple string of Ruby to be executed, a Proc, or a method name as a symbol as a value to either :if or :unless in the options for your validation. Here are some examples:
Prior to Rails version 5.2 you could pass a string:
# using a string:
validates :name, uniqueness: true, if: 'name.present?'
From 5.2 onwards, strings are no longer supported, leaving you the following options:
# using a Proc:
validates :email, presence: true, if: Proc.new { |user| user.approved? }
# using a Lambda (a type of proc ... and a good replacement for deprecated strings):
validates :email, presence: true, if: -> { name.present? }
# using a symbol to call a method:
validates :address, presence: true, if: :some_complex_condition
def some_complex_condition
true # do your checking and return true or false
end
In your case, you could do something like this:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id, unless: Proc.new { |question| question.user.is_admin? }
end
Have a look at the conditional validation section on the rails guides for more details: http://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
The only way I know of to guarantee uniqueness is through the database (e.g. a unique index). All Rails-only based approaches involve race conditions. Given your constraints, I would think the easiest thing would be to establish a separate, uniquely indexed column containing a combination of the day and user id which you'd leave null for admins.
As for validates_uniqueness_of, you can restrict validation to non-admins through use of an if or unless option, as discussed in http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
Just add a condition to the validates_uniqueness_of call.
validates_uniqueness_of :created_on, scope: :user_id, unless: :has_posted?
def has_posted
exists.where(user_id: user_id).where("created_at >= ?", Time.zone.now.beginning_of_day)
end
But even better, just create a custom validation:
validate :has_not_posted
def has_not_posted
posted = exists.where(user: user).where("DATE(created_at) = DATE(?)", Time.now)
errors.add(:base, "Error message") if posted
end

Rails 3: how to generate custom error message from failed validation

I'm using
validates :feed_id, presence: true, uniqueness: true
How should I be generating a custom error message to specify that the user has already subscribed to this feed (the feed_id) field is a duplicate
I know I can just do validate_uniqueness_of but it would clutter up the code unnecessarily. How do I pass a specific error message if uniqueness validation fails??
Put a hash with the key message and desired message as the value instead of true:
validates :feed_id, presence: true, uniqueness: {message: "already subscribed"}

Resources