I have this basic validation in my model:
validates :student_number, :presence => true,
:length => { :maximum => 255 },
:uniqueness => true
So what is all that? Here's my best guess, if you would kindly tell me where I'm mistaken, I'd appreciate it.
validates is a method. I send it the symbol :first_name, then :presence => true, which is...a hash with :presence for a key and true as a value?
Except it doesn't really look like a hash, at least not according to the docs.
And then :length => { :maximum => 255 } is the same sort of entity (hash?) as :presence => true but it expects another hash as an argument?
Thanks for any assistance.
Ruby allows you to drop parentheses and brackets if it can infer their locations by itself; in your case, you could rewrite the code as:
validates(:student_number, { :presence => true,
:length => { :maximum => 255 },
:uniqueness => true })
which is a method call, passing a first argument which is the attribute to validate, and a second argument which is the validation options, a hash.
Note: This explanation is a bit of a simplification, validates is actually a bit more complicated in how it handles its arguments. See here for more details on how this works exactly.
close but not close enough. All :presence => true, :length => { :maximum => 255 }, :uniqueness => true is ONE hash with three keys presence, length, uniqueness and three coresponding values. In fact it is the same as you would write
{ :presence => true, :length => { :maximum => 255 }, :uniqueness => true } but first way is shorter
Related
I am new to rails and currently learning about validations, so i have created a form within which i have a field named no (type integer), in the model for validation i have done something like this:
validates :no,
:presence => true,
:uniqueness => true,
:numericality => { :only_integer => true, :greater_than_or_equal_to => 1, :less_than_or_equal_to => 99999 }
now when nothing is entered than two error messages are displayed
1]Noを入力してください。 -> please input the no.
2]Noは数値で入力してください。 -> please enter the number in integer only.
sorry abt the japanese stuff as my os is in japanese ;-)
what i need is that when the 'no' field is empty it should only display the error_message for that emptiness. Currently it is displaying error_message for both presence & numericality when the field is just empty.
I'm sorry if this is a really basic question I have tried searching for answers but I can't seem to find any.
change your validation for numericality to only work when that field is present.
validates :no,
:presence => true,
:uniqueness => { :if => :no_is_present? },
:numericality => {
:only_integer => true,
:greater_than_or_equal_to => 1,
:less_than_or_equal_to => 99999,
:if => :no_is_present?
}
def no_is_present?
no.present?
end
Does validates :uniqueness get called every time an object is saved even if a field has not changed? Isn't this a performance issue?
validates :name, :schedule_id, :uniqueness => true
It seems to be the case that it does. So isn't it almost always necessary to make sure a change has taken place before running the validation? As every field being checked for uniqueness requires a database hit.
This would be better:
validates :name, :schedule_id, :uniqueness => true, :if => "name_changed? || schedule_id_changed?"
And this much better, if a bit more verbose:
validates :name, :uniqueness => true, :if => :name_changed?
validates :schedule_id, :uniqueness => true, :if => schedule_id_changed?
Gist here: https://gist.github.com/4017019
try this
validates :name, :uniqueness => true, :if => lambda {self.name_changed? }
I have a restrictive schema (i.e. the schema already has null restrictions and maximum length etc). Would putting all of them in the model also overkill and counterproductive?...
validates :CouponID, :presence => true,
:numericality => true
validates :MerchantName, :presence => true,
:length => { :maximum => 100 }
validates :MerchantID, :presence => true,
:numericality => true
validates :Network, :length => { :maximum => 20 }
validates :Label, :presence => true
validates :CouponCode, :length => { :maximum => 100 }
validates :EndDate, :presence => true
validates :Link, :presence => true
validates :Status, :presence => true,
:length => { :maximum => 45 }
validates :Country, :length => { :maximum => 100 }
No it's not an overkill. Putting these into validators your model would allow Rails to catch them before inserting them into the database. It's also good design and practice.
If you omitted these, you would get MySQL errors thrown instead.
For example. Let's say I have a Comment model which contains an attribute string called body which can not be nil in my table.
class Comment < ActiveRecord::Base
end
If I tried:
comment = Comment.create(body: nil)
I would get the following exception.
ActiveRecord::StatementInvalid: Mysql2::Error: Column 'body' cannot be null:
This is bad. The natural flow of your application would break.
But, if I put the validators in my model like so
class Comment < ActiveRecord::Base
validates :body, presence: true
end
and tried the following:
comment = Comment.create(body: nil)
I would not get an exception thrown but the errors array for my variable to tell me what went wrong.
comment.errors.full_messages
=> ["Body can't be blank"]
It's good practice to put validators in your models and allows for good design.
I have a Message.uuid field which I want to add validations for which include:
Supported:
A-Z, a-z, 0-9
dashes in the middle but never starting or ending in a dash.
At least 5, no more than 500 characters
What is the best way in rails to write a model validation for these rules?
Thanks
UPDATE:
validates :uuid,
:length => { :within => 5..500 },
:format => { :with => /[A-Za-z\d][-A-Za-z\d]{3,498}[A-Za-z\d]/ }
With a valid UUID this is failing
I'd leave the length validation up to a validates_length_of validator, so that you get more specific error messages. This will do two things for you: Simplify the regex used with your validates_format_of validator, and provide a length-specific error message when the uuid is too short/long rather than showing the length error as a "format" error.
Try the following:
validates_length_of :uuid, :within => 5..500
validates_format_of :uuid, :with => /^[a-z0-9]+[-a-z0-9]*[a-z0-9]+$/i
You can combine the two validations into a single validates with Rails 3:
validates :uuid,
:length => { :within => 5..500 },
:format => { :with => /^[a-z0-9][-a-z0-9]*[a-z0-9]$/i }
Use:
validates :uuid, :format => {:with => /my regexp/}
As for the regexp, you've already asked for it in another question.
I've got a project where there is a CURRENCY and COUNTRY table. There's a PRICE model that requires a valid currency and country code, so I have the following validation:
validates :currency_code, :presence => true, :inclusion => { :in => Currency.all_codes }
validates :country_code, :presence => true, :inclusion => { :in => Country.all_codes }
The all_codes method returns an array of just the currency or country codes. This works
fine so long as no codes are added to the table.
How would you write this so that the result of the Currency.all_codes was either a Proc or inside a lambda? I tried Proc.new { Currency.all_codes } -- but then get an error that the object doesn't respond to include?
Just use a proc, like so:
validates :currency_code,
:presence => true,
:inclusion => { :in => proc { Currency.all_codes } }
validates :country_code,
:presence => true,
:inclusion => { :in => proc { Country.all_codes } }
It's worth noting for anyone else who may stumble across this that the proc also has the record accessible as a parameter. So you can do something like this:
validates :currency_code,
:presence => true,
:inclusion => { :in => proc { |record| record.all_codes } }
def all_codes
['some', 'dynamic', 'result', 'based', 'upon', 'the', 'record']
end
Note: This answer is true for old versions of Rails, but for Rails 3.1 and above, procs are accepted.
It must not accept Procs. You can use a custom validation method to do the same thing:
validate :currency_code_exists
def currency_code_exists
errors.add(:base, "Currency code must exist") unless Currency.all_codes.include?(self.currency_code)
end