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.
Related
Is there a difference between using
validates :foo, uniqueness: true
or
validates_uniqueness_of :foo?
I know this is a simple questions, but Google didn't help
When and why should one be used over the other?
The validates method is a shortcut to all the default validators that Rails provides. So, validates :foo, uniqueness: true would trigger UniquenessValidator under the hood. The source code for validates can be found in the API doc here. As shown there, it basically triggers the validators of the options passed and raises an error in case an invalid option is passed.
validates_uniqueness_of also triggers the UniquenessValidator, the same as validates. Its source code is
# File activerecord/lib/active_record/validations/uniqueness.rb, line 233
def validates_uniqueness_of(*attr_names)
validates_with UniquenessValidator, _merge_attributes(attr_names)
end
The only difference is that with validates_uniqueness_of, we can ONLY validate the uniqueness and not pass additional options, whereas validates accepts multiple options. So we could have the following validations with validates:
validates :name, presence: true, uniqueness: true, <some other options>
But the same would not be possible with validates_uniqueness_of.
lets say that I want to make the possibility of posting two types of posts [long] and [short] sharing the same table [id, title, content, short:boolean, user_id] and model
and the user chose to post short post and the site only will store the content and it will be under 120 char, to recognize it we will put the short:true, but how to customize the validations if it chose short to allow empty title and content under 120 char ..... etc
Simplest way to solve your problem is conditional validations. Your model should be like this:
class Post < ActiveRecord::Base
validates :title, presence: true, unless: :short?
validates :title, absence: true, if: :short?
validates :content, presence: true
validates :content, length: { maximum: 120 }, if: :short?
end
I'm not sure i understood all conditions in your example right, hope this code is enough to make what you want.
Also you may read details about conditional validations in Rails documentation.
Since you have a short boolean field on Post, you can simply add these helper methods
class Post < AR
# this method is automatically generated by rails
def short?
short
end
def long?
!short
end
end
And then add if: or unless: to your validations:
validates :content, length: { maximum: 120 }, if: :short?
validates :content, presence: true, if: :long?
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
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...
I'm trying to figure out the difference between:
validates :foo, presence: false
validates :foo, allow_blank: true
When I use presence: false validation fails but when I use allow_blank: true it does not. According to the docs http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_presence_of uses the blank? method. Can someone please explain the difference?
First case:
validates :foo, presence: false
it does not validate the presence of :foo at all.
nil, '', 'anything' are all valid.
Second case: :allow_blank is an option, not a validator.
It skips validation if the attribute is blank (more here).
If you want to know how it works, you can see the code here.
Before call the selected validator, it checks the attribute is not blank, if it's then skip validation.
The only reason why it works as a validator is the way that source code is written.
At any moment Rails' team can change the code and :allow_blank stop working as a validator.
allow_blank only validates nil, presence validates nil as well as empty