I have a simple validation in one of my Rails models, but it doesn't seem to be triggering an error like I want.
before_save :check_future_date
private
def check_future_date
puts "=============================================================="
puts self.article.date
puts Date.today
if self.article.date <= Date.today
puts "error!!!!!!!!!!!!!!!!!"
errors[:base] << "Sorry, you must post at least a day in advance"
end
end
I know that the logic is being triggered because in the console, I see:
==============================================================
2013-04-06
2013-04-29
error!!!!!!!!!!!!!!!!!
However, the record is still saved, and no error message is shown. I have also tried:
errors.add(:base, 'Sorry, you must post at least a day in advance')
You need to register the custom validation method, instead of performing a before_save.
Change
before_save :check_future_date
To
validate :check_future_date
Related
If user inserts a value that is not accepted to the to_date method, e.g. "some gibberish", it returns:
"ArgumentError: invalid date"
How could I construct a custom validation that returns an validation message to user and doesn't halts the application?
I was thinking that reusing the code from another answer would be a good idea, but I don't know how to apply it to to_date.
validate do
self.errors[:start] << "must be a valid date" unless DateTime.parse(self.start) rescue false
end
It could be achieved with rescue, and then addes to errors
class Post < ActiveRecord::Base
validate do |post|
begin
Date.parse(post.date)
rescue ArgumentError
errors.add(:date, "must be a valid date")
end
end
## Another way
validate do |post|
# make sure that `date` field in your database is type of `date`
errors.add(:date, "must be a valid date") unless post.date.try(to_date)
end
end
Rather than writing your own validation (and having to write tests for it!) I suggest the validates_timeliness gem
Using it is as simple as
validates_date :attribute_name
I have a model where if it is given a certain status, the status can never be changed again. I've tried to achieve this by putting in a before_save on the model to check what the status is and raise an exception if it is set to that certain status.
Problem is this -
def raise_if_exported
if self.exported?
raise Exception, "Can't change an exported invoice's status"
end
end
which works fine but when I initially set the status to exported by doing the following -
invoice.status = "Exported"
invoice.save
the exception is raised because the status is already set the exported on the model not the db (I think)
So is there a way to prevent that attribute from being changed once it has been set to "Exported"?
You can use an validator for your requirement
class Invoice < ActiveRecord::Base
validates_each :status do |record, attr, value|
if ( attr == :status and status_changed? and status_was == "Exported")
record.errors.add(:status, "you can't touch this")
end
end
end
Now
invoice.status= "Exported"
invoice.save # success
invoice.status= "New"
invoice.save # error
You can also use ActiveModel::Dirty to track the changes, instead of checking current status:
def raise_if_exported
if status_changed? && status_was == "Exported"
raise "Can't change an exported invoice's status"
end
end
Try this, only if you really want that exception to raise on save. If not, check it during the validation like #Trip suggested
See this page for more detail.
I'd go for a mix of #Trip and #Sikachu's answers:
validate :check_if_exported
def check_if_exported
if status_changed? && status_was.eql?("Exported")
errors.add(:status, " cannot be changed once exported.")
end
end
Invalidating the model is a better response than just throwing an error, unless you reeeally want to do that.
Try Rails built in validation in your model :
validate :check_if_exported
def check_if_exported
if self.exported?
errors.add(:exported_failure, "This has been exported already.")
end
end
If a user tries to enter a duplicate entry into a table, they get a full page nasty error
ActiveRecord::RecordNotUnique in Admin::MerchantsController#add_alias
Mysql2::Error: Duplicate entry '2-a' for key 'merchant_id': ..
Is there a way to give a flash message instead like "This record already exists"?
This thread from railsforum can help you
I wouldn't recommend checking for the uniqueness and specifically responding to this validation rule via a flash message, this conventionally should be handled by the form's error messages instead.
Nonetheless, in the controller actions for creating and updated you could wrap the conditional block which decides where to send the user, with a check for uniqueness first:
def create
#merchant = Merchant.new params[:merchant]
if Merchant.where(:merchant_id => #merchant.id).count > 0
flash[:error] = "The merchant id #{#merchant.id} already exists"
render :create # amend this to :update when applying to the update action
else
# your normal generated save and render block
end
end
This isn't the cleanest way of achieving your goal, but I think it'll be easiest to understand.
Would really recommend the model validations and form error messages instead, which if you are usung the generated scaffolding, all you need to do is add a model validation and the form throw out the error messages to the user for you:
# app/models/merchant.rb
class Merchant < ActiveRecord::Base
validates_uniqueness_of :merchant_id
end
How can I add errors to the User model manually?
Is it just #user.errors << "some text goes here" ?
#user.errors.add(:email, "Not valid")
If you don't want to use any attributes, then in your model
#user.errors[:base] << "This person is invalid because ..."
For details: link
WARNING
If you just add errors in a separate method (not in a validation), then by default when you call .valid? or .save those errors will be automatically cleared. So you may want to use validation contexts instead.
Use the errors.add method
Example:
#user.errors.add(:name, "wasn't filled in")
try this:
errors.add(:base, "#{user.full_name} has errors here!")
Expanding on Jason Axelson's answer:
Manually added errors (e.g. errors.add(:email, 'invalid')) will not affect .save or .valid?.
You can use validation contexts to run conditional validations:
class User < ActiveRecord::Base
validate :ensure_something, on: :special_context
private
def ensure_something
errors.add(:email, 'invalid') if special_condition
end
end
user.save!(context: :special_context)
user.valid?(context: :special_context)
I just upgraded my rails to 2.3.4 and I noticed this with validations:
Lets say I have a simple model Company which has a name. nothing to it.
I want to run my own validation:
class Company < ActiveRecord::Base
validate :something
def something
false
end
end
saving the model actually works in this case.
The same thing happens if i override validate() and return false.
I noticed this in a more complex model where my validation was returning false, but the object was still saving...I tried it out in an essentially empty model and the same thing applied. Is there a new practice I am missing? This doesn't seem to be the case in some of my older rails code.
Your validations are executed when you use the validate method. However rails doesn't relies on the returned value.
It relies on if there are validations errors or not. So you should add errors when your model doesn't validates.
def something
errors.add(:field, 'error message')
end
Or, if the error is not related to a field :
def something
errors.add(:base, 'error message')
end
Then your model won't be saved because there are errors.
You're getting confused between validations and callbacks.
Validations are supposed to fail if there are any errors on the object, doesn't matter what the validation returns. Callbacks fail if they return false, regardless if they add any errors to object.
Rails uses calls valid? from save calls which does not check the result of any validations.
Edit: Rails treats validate :method as a callback, but valid? still doesn't check for their results, only for errors they added to the object.
I don't think this behaviour changed at all but I could be wrong. I don't think I've ever written a validation to return false before.
Just FYI errors.add_to_base('error message') has been deprecated in rails 3 and got replaced by
errors[:base] << "Error message"
Or
errors.add(:base, "Error message")