I'm trying to call validation on an ActiveRecord after a certain method in another Ruby file is called. Is there some way I can tie this into ActiveRecord's validation scheme, i.e.:
validate :cars_have_wheels?, on: after_cache_reset
Note: cars_have_wheels? is a method located in the ActiveRecord object, after_cache_reset is the method in the other file.
Thank you!
Check out this link (Ruby on Rails guides):
5 Conditional Validation
Sometimes it will make sense to validate an object only when a given predicate is satisfied. You can do that by using the :if and :unless options, which can take a symbol, a string, a Proc or an Array. You may use the :if option when you want to specify when the validation should happen. If you want to specify when the validation should not happen, then you may use the :unless option.
5.1 Using a Symbol with :if and :unless
You can associate the :if and :unless options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.
class Order < ActiveRecord::Base
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
end
Related
I have a simple capitalize method so that when user submits a new band in the band page it returns it with the first letter capitalized.
Inside my Band class I also have a validates_uniqueness_of :band_name to see if there is already a band with the same entry. See code below:
class Band < ActiveRecord::Base
has_and_belongs_to_many :venues
validates :band_name, :presence => true
before_save :title_case
validates_uniqueness_of :band_name
private
def title_case
self.band_name.capitalize!
end
end
So if I type in someband, it creates it and displays it as Someband. If I type someband again, ActiveRecord sees it as unique and I'll get another Someband. The only way it works is if I type Someband. How would I remedy this situation?
I think what you want to do is this
validates_uniqueness_of :band_name, :case_sensitive :false, allow_blank: false
Take a look at http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html
:case_sensitive - Looks for an exact match. Ignored by non-text
columns (true by default).
The reason your code doesn't work is because validations happen before the before_save callbacks are triggered. Check out the list of ActiveRecord::Callbacks for the order in which things are called.
MZaragoza's answer is a great option for making your validation work regardless of what casing your users might enter. It will prevent things like "someband" and "SomeBand" from being added. I recommend including that as part of your solution.
Another option very similar to the code you already have is to switch to using the before_validation callback:
before_validation :title_case
I highly recommend using the before_validation callbacks instead of before_save callbacks whenever data changes that may be relevant to your validation rules, regardless of what other changes you make. That ensures that you are checking that actual state of the model that you plan to save to the database.
You can use attribute setter instead of before_save callback to capitalize your value without postponing.
def band_name=(value)
self['band_name'] = value && value.mb_chars.capitalize
end
I have a Rails 3.2.18 app where I'm trying to do some conditional validation on a model.
In the call model there are two fields :location_id (which is an association to a list of pre-defined locations) and :location_other (which is a text field where someone could type in a string or in this case an address).
What I want to be able to do is use validations when creating a call to where either the :location_id or :location_other is validated to be present.
I've read through the Rails validations guide and am a little confused. Was hoping someone could shed some light on how to do this easily with a conditional.
I believe this is what you're looking for:
class Call < ActiveRecord::Base
validate :location_id_or_other
def location_id_or_other
if location_id.blank? && location_other.blank?
errors.add(:location_other, 'needs to be present if location_id is not present')
end
end
end
location_id_or_other is a custom validation method that checks if location_id and location_other are blank. If they both are, then it adds a validation error. If the presence of location_id and location_other is an exclusive or, i.e. only one of the two can be present, not either, and not both, then you can make the following change to the if block in the method.
if location_id.blank? == location_other.blank?
errors.add(:location_other, "must be present if location_id isn't, but can't be present if location_id is")
end
Alternate Solution
class Call < ActiveRecord::Base
validates :location_id, presence: true, unless: :location_other
validates :location_other, presence: true, unless: :location_id
end
This solution (only) works if the presence of location_id and location_other is an exclusive or.
Check out the Rails Validation Guide for more information.
I have a form that allow me to edit an entry. I have a secondary set of attributes that must all be here if one of them have a value. I use conditional validates to do so :
validates_presence_of :raison_sociale,:nom_contact,
:prenom_contact,:telephone,
if: (:nom_contact?||:raison_sociale?||
:prenom_contact? || :telephone?)
But the strange thing is, those 4 fields are not evaluated the same way! If i remove nom_contact, it save. But if i remove it and telephone, it fails.
What i observed is that it was ignoring a blank field if it was the first one in the if condition! As soon as i put prenom contact as first condition i cannot save without nom_contact, but now it's prenon_contact that is ignored!
Why does my conditions behave strangly and what can i do?
What ca i do to avoid that?
You could put the if validation in a proc.
"Symbol-only" conditional validation expects a symbol for the name of a method that will be called.
You're including conditionals, which AFAIK won't work without being in a proc.
You can do some combinations by using an array, as per the docs:
class Computer < ActiveRecord::Base
validates :mouse, presence: true,
if: ["market.retail?", :desktop?]
unless: Proc.new { |c| c.trackpad.present? }
end
But those are and, not or.
I'd probably wrap it up in a method anyway; IMO it's a bit long for an in-line block, but that's more a matter of opinion.
I'm trying to update single attribute of a user model from a admin controller (not users controller).
While doing this I tried update_attribute() but it was changing the users password also.
I think the password is changing because I have before_save method on user model which hashes the password.
update_attributes() is not working because it is checking the validations for password which is presence=>true
Is there any way to achieve this?
You can set a condition on your validations by using the :if option. In my code, it looks something like this:
validates :password,
:length => { :minimum => 8 },
:confirmation => true,
:presence => true,
:if => :password_required?
def password_required?
crypted_password.blank? || password.present?
end
So basically, it's only if the crypted_password in the database is not set (meaning a new record is being created) or if a new password is being provided that the validations are run.
Try update_column(name, value), it might work.
You can update single attribute of user like this
#user is that user whose attribute you want to update
e.g user_name
#user.update_attributes(:user_name => "federe")
Try it and it will only update one attribute..
ActiveRecord has an 'update-column' method that skips both validations and callbacks:
http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_column
However, I'd suggest that could be dangerous - you have that :before_save filter for a reason. If you place an :except method on the filter to circumvent in specific cases, it not only becomes reusable but you keep behaviour consistent and avoid having a method buried in a controller that's bypassing your Model's validation/callback stack.
I'm personally not overly keen on seeing methods like update_column anywhere except as protected methods inside Models.
Try :
To bypass callback and validations use :
User.update_all({:field_name => value},{:id => 1})
Just wanted to let you know :
In Rails, update_attribute method bypasses model validations, while update_attributes and update_attributes! will fail (return false or raise an exception, respectively) if a record you are trying to save is not valid.
The difference between two is update_attribute use save(false) where as update_attributes uses save or you can say save(true) .
I am trying to make a before_save in a rails app conditional, but it doesn't seem to be working.
before_save method_call_to_run if self.related_model.some_method_that_returns_t_or_f?
If the 'some_method_that_returns_t_or_f' returns true, I want it to run the method before it saves the object otherwise I just want it to ignore the before_save.
you can use :if
before_save do_something, :if => Proc.new {|model| model.some_boolean_attr_or_method }
or simply
before_save do_something, :if => some_condition
EDIT:
for a quick reference, there's an excellent guide about this:
http://guides.rubyonrails.org/active_record_callbacks.html#conditional-callbacks
Rails 5
I've had success defining a private method which contains the boolean logic and then passing it as a symbol (that last part seems like a requirement):
before_save do_something, if: :private_boolean_method?
I also recently found out you can simply pass a block (took me a while to figure out the syntax):
before_save do_something, if: -> { condition == "met" }