Validation only in specific form - ruby-on-rails

Is there any way to trigger validation only in specific forms(controller's action), not globally at every save or update?
Something like User.create(:validate=>true) flag.

Yes, you can supply conditionals to the validations, eg:
validates_presence_of :something, :if => :special?
private
def make_sepcial
#special = true
end
def special?
#special
end
Now all you have to do to turn on these validations is:
s = SomeModel.new
s.make_special

As you explained in the comments, you want to skip validation for new records. In that case, you can use thomasfedb's answer, but don't use the #special variable, but:
validates_presence_of :something, :if => :persisted?
This will validate only for saved Users, but not for new Users. See the API documentation on persisted?.

This is a bit old. But I found http://apidock.com/rails/Object/with_options to be a good way of handling this sort of behaviour.

Related

Can you make an optional model attribute required for a form?

Is it possible to make a attribute required just for a particular form?
The field is nullable in the model, and there is no validation setup for it currently and I want to keep it that way.
But on 1 form, I would like to make the field required.
Is this possible without creating a separate model for this?
You could use ActionControllers require for the used parameters, something like this:
def person_params
params.require(:person).permit(:name).tap do |person_params|
person_params.require(:name) # SAFER
end
end
http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-require
Does that help you?
I know that you doesn't want to touch the model, but you can do a conditional validation like this that isn't invasive
validates_presence_of :your_attribute, :if => :from_specific_form?
and create some methods that work with this
private
def from_form
#from_specific_form = true
end
def from_specific_form?
#from_specific_form
end
Then when you want the validation to work, just do something like this
xyz = YourModel.new
xyz.from_form

update_attribute/s() is calling callback for save password

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) .

How to Skip Validations w/ find_or_create_by_?

Is it possible to skip validations with a dynamic find/create by method?
For example with regular save I can do something like:
p = Post.new
p.title = nil
p.body = nil
p.save(:validate => false)
Would love to do the same with find_or_create_by_title.
It dosnt look possible with the code in Rails right now however you may have better luck being a little more verbose in how you write the code. You can use find_or_initialize_by_ which creates a new object but does not save it. You can then call save with your custom options, also in the documentation they have a neat demonstration that is hard to find so I will include it below:
# No 'Winter' tag exists
winter = Tag.find_or_initialize_by_name("Winter")
winter.new_record? # true
Good luck and let me know if you need more pointers in the right direction.
For some cases, find_or_initialize_by_ will not be useful and need to skip validations with find_or_create_by.
For this, you can use below alternative flow and method of ROR:
Update your model like this:
class Post < ActiveRecord::Base
attr_accessor :skip_validation
belongs_to :user
validates_presence_of :title, unless: :skip_validation
end
You can use it now like this:
Post.where(user_id: self.id).first_or_create!(skip_validation: true)
I have used first_or_create instead of find_or_create_by here. You can pass more column names and values with this, and your validation will not be worked with this.
You can continue without any changes for strong parameters end and no need to permit this 'skip_validation' so it will work with validations while adding entries.
Using this, you can use it with and without validations by passing a parameter.
Currently skipping validation DOES work with find_or_create_by.
For example, running:
Contact.find_or_create_by(email: "hello#corsego.com).save(validate: false)
will skip a validation like:
validates :name, :email, presence: true, uniqueness: true

Validate only when

I need to validate a value's presence, but only AFTER the value is populated. When a User is created, it is not required to set a shortcut_url. However, once the user decides to pick a shorcut_url, they cannot remove it, it must be unique, it must exist.
If I use validates_presence_of, since the shortcut_url is not defined, the User isn't created. If I use :allowblank => true, Users can then have "" as a shortcut_url, which doesn't follow the logic of the site.
Any help would be greatly appreciated.
Here we are always making sure the shortcut_url is unique, but we only make sure it is present if the attribute shortcut_selected is set (or if it was set and now was changed)
class Account
validates_uniqueness_of :shortcut_url
with_options :if => lambda { |o| !o.new_record? or o.shortcut_changed? } do |on_required|
on_required.validates_presence_of :shortcut_url
end
end
You'll need to test to make sure this works well with new records.
Try the :allow_nil option instead of :allow_blank. That'll prevent empty strings from validating.
Edit: Is an empty string being assigned to the shortcut_url when the user is being created, then? Maybe try:
class User < ActiveRecord::Base
validates_presence_of :shortcut_url, :allow_nil => true
def shortcut_url=(value)
super(value.presence)
end
end
try conditional validations, something like:
validates_presence_of :shortcut_url, :if => :shortcut_url_already_exists?
validates_uniqueness_of :shortcut_url, :if => :shortcut_url_already_exists?
def shortcut_url_already_exists?
#shortcut_url_already_exists ||= User.find(self.id).shortcut_url.present?
end

Validate model for certain action

I need to validate a model only for a certain action (:create). I know this is not a good tactic, but i just need to do this in my case.
I tried using something like :
validate :check_gold, :if => :create
or
validate :check_gold, :on => :create
But i get errors. The problem is that i cannot have my custom check_gold validation execute on edit, but only on create (since checking gold has to be done, only when alliance is created, not edited).
Thanx for reading :)
I'm appending some actual code :
attr_accessor :required_gold, :has_alliance
validate :check_gold
validate :check_has_alliance
This is the Alliance model. :required_gold and :has_alliance are both set in the controller(they are virtual attributes, because i need info from the controller). Now, the actual validators are:
def check_gold
self.errors.add(:you_need, "100 gold to create your alliance!") if required_gold < GOLD_NEEDED_TO_CREATE_ALLIANCE
end
def check_has_alliance
self.errors.add(:you_already, "have an alliance and you cannot create another one !") if has_alliance == true
end
This works great for create, but i want to restrict it to create alone and not edit or the other actions of the scaffold.
All ActiveRecord validators have a :on option.
validates_numericality_of :value, :on => :create
Use the validate_on_create callback instead of validate:
validate_on_create :check_gold
validate_on_create :check_has_alliance
Edit:
If you use validates_each you can use the standard options available for a validator declaration.
validates_each :required_gold, :has_alliance, :on => :create do |r, attr, value|
r.check_gold if attr == :required_gold
r.check_has_alliance if attr == :has_alliance
end
Like Sam said, you need a before_create callback. Callbacks basically mean 'execute this method whenever this action is triggered'. (More about callbacks here : http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html).
This is what you want in your model:
before_create :check_gold
# other methods go here
private # validations don't need to be called outside the model
def check_gold
# do your validation magic here
end
The above method is the simplest to do what you want, but FYI there's also a way to use a before_save callback to execute additional actions on creation:
before_save :check_gold_levels
# other methods
private
def check_gold_levels
initialize_gold_level if new? # this will be done only on creation (i.e. if this model's instance hasn't been persisted in the database yet)
verify_gold_level # this happens on every save
end
For more info on 'new?' see http://api.rubyonrails.org/classes/ActiveResource/Base.html#method-i-new%3F
You need to look into callbacks. Someone once told me this and I didn't understand what they meant. Just do a search for rails callbacks and you will get the picture.
In your model you need to do a callback. The callback you need is before_create and then before a object is created you will be able to do some logic for check for errors.
model.rb
before_create :check_gold_validation
def check_gold_validation
validate :check_gold
end
def check_gold
errors.add_to_base "Some Error" if self.some_condition?
end

Resources