I'm trying to get my custom validation to work on create. But when I do a find then save, rails treats it as create and runs the custom validation. How do I get the validations to only work when creating a new record on not on the update of a found record?
Try this on your line of validation code:
validate :custom_validation, on: :create
this specifies to only run the validation on the create action.
Source:
ActiveModel/Validations/ClassMethods/validate # apidock.com
TL;DR; Full example:
class MyPerson < ActiveRecord::Base
validate :age_requirement
private
def age_requirement
unless self.age > 21
errors.add(:age, "must be at least 10 characters in length")
end
end
end
To have the validator run only if a new object is being created, you can change the 2nd line of the example to: validate :age_requirement, on: :create
Only on updates: validate :age_requirement, on: :update
Hope that helps the next person!
Related
In Rails 5 I can't seem to set a field without having the validation fail and return an error.
My model has:
validates_presence_of :account_id, :guid, :name
before_save :set_guid
private
def set_buid
self.guid = SecureRandom.uuid
end
When I am creating the model, it fails with the validation error saying guid cannot be blank.
def create
#user = User.new(new_user_params)
if #user.save
..
..
private
def new_user_params
params.require(:user).permit(:name)
end
2
Another issue I found is that merging fields doesn't work now either. In rails 4 I do this:
if #user.update_attributes(new_user_params.merge(location_id: #location_id)
If I #user.inspect I can see that the location_id is not set. This worked in rails 4?
How can I work around these 2 issues? Is there a bug somewhere in my code?
You have at least two options.
Set the value in the create action of your controller
Snippet:
def create
#user = User.new(new_user_params)
#user.guid = SecureRandom.uuid
if #user.save
...
end
In your model, use before_validation and add a condition before assigning a value:
Snippet:
before_validation :set_guid
def set_guid
return if self.persisted?
self.guid = SecureRandom.uuid
end
1
Use before_validation instead:
before_validation :set_guid
Check the docs.
2
Hash#merge works fine with rails ; your problem seems to be that user is not updating at all, check that all attributes in new_user_params (including location_id) ara valid entries for User.
If update_attributes fails, it will do so silently, that is, no exception will be raised. Check here for more details.
Try using the bang method instead:
if #user.update_attributes!(new_user_params.merge(location_id: #location_id))
I have a validation method that should validate whether an user is part of a team. If it is not part of the team it should add an error and thus failing to save the record.
This is the current method that is inside the model:
def assignee_must_be_part_of_team
unless assignee_id.blank?
team = Team.find(self.team_id)
errors.add(:team, 'Equipe não existe') unless team
user = team.users.find(self.assignee_id)
errors.add(:assignee_id, 'Responsável não faz parte da equipe') unless user
end
end
And I am registering it in my model with this:
validate :assignee_must_be_part_of_team, on: :save
However, this method is not being even called when I save a new record! I even tried to add some logs to it but nothing happens and the record is being saved anyway.
Am I missing anything here?
Use create or update as the value of :on option.
Change this:
validate :assignee_must_be_part_of_team, on: :save
To:
validate :assignee_must_be_part_of_team, on: :create
or:
validate :assignee_must_be_part_of_team, on: :update
If you want your validation to run for both create and update actions, then you don't even need to specify the :on option at all, because that's the default behaviour. So, just this should work:
validate :assignee_must_be_part_of_team
See the documentation here for more information.
You are adding two errors in one validation. maybe you can split this into separate validations for easy debugging:
validates :team_id, presence: :true
validate :belong_to_team, :assignee_part_of_team
private
def belong_to_team
errors[:team] << 'Equipe não existe' unless self.team
end
def assignee_part_of_team
errors[:assignee] << 'Responsável não faz parte da equipe' unless self.team and self.team.users.include?(self.assignee)
end
Then you can know which is causing the fault here.
I'm using acts_as_taggable_on plugin in conjunction with my User model.
acts_as_taggable_on :skills
Now, I have a custom controller called SkillsController to add skills via ajax.
class SkillController < ApplicationController
def add
current_user.skill_list.add(params[:skill])
current_user.save # Not saving!
end
end
and in routes.rb
get 'skill/:skill', to: 'skill#add'
I guess it has to do something with Strong Parameters, but I don't know how to solve it as it stands.
The current_user.save isn't working, how to solve it.
P.S current_user.errors shows #message is "too short" as per my validations. But how do I just save the skill_list without having to modify other attributes or running validations on them?
If you want to save current_user without validation check you can do just like as:
current_user.save(:validate => false)
This will work for you :)
I have a rails project, using active record and was wondering if there was a validation helper for max number of individual entries. For example, if you had a form submission and only wanted, say 2, and you just wanted the first 2 to be persisted to the table, how would you do this?
I have read the manual and had a look at numericality etc but it's not really what I'm looking for. I have tried to write my own validation method in the model, but I am assuming there is a validation helper that makes this easier:
def validatePassengerNumber
if self.passengers.length > 2
# unsure on how to refuse new data access to database
end
end
Add an error to base after check return true, and it will prohibit to save into database.
def validate_passenger_number
self.errors.add(:base, "exceed maximum entry") if self.passengers.length > 2
end
Call this custom validation in respective model.
validate :validate_passenger_number, on: :create
There's no built-in validation; at least I haven't come across any. But following is a way to impose this type of validation:
def max_passengers
if self.passengers.count > 2
errors.add_to_base("There should not be more than 2 passengers.")
end
end
And then, you can use this validation to impose a check on the number of passengers.
You can add callback and validate the record.
before_validation :validate_passanger_number
private
def validate_passanger_number
errors.add("Sorry", "you have added maximum passengers.") if self.passengers.count > 2
end
Lets say I have the following model:
class Car < ActiveRecord::Base
attr_accessible :wheels,
:engine_cylinders
validates :wheels, :engine_cylinders, presence: true, numericality: true
end
Lets say I then have the following controller action:
#car = Car.find(params[:id])
#car.wheels = "foo"
#car.engine_cylinders = 4
#car.save
This save will fail as wheels will fail the numericality condition.
Is there any way to persist the succesful attributes (in this case engine_cylinders), while adding the invalid attributes to the errors array? E.g. is there a "soft" validation in Rails?
You want to write a Custom Validator.
class Car < ActiveRecord::Base
validate :wheel_range,
:engine_cylinder_range
def engine_cylinder_range
flash[:notice] = "engine_cylinder was not saved because it wasn't a number" unless engine_cylinder.is_a? Fixnum
# set engine_cylinder back to old value
end
def wheel_range
flash[:notice] = "wheels was not saved because it wasn't a number" unless wheels.is_a? Fixnum
# set wheels back to old value
end
end
You don't have to use flash here, you could use any variable for internal processing or re-display.
You may also want to put this custom validation check on the :before_save hook. Use the _was magic method to get the old value.
If you're looking to bypass validations you can always do so with:
if #car.save
# ...
else
#car.save(validate: false)
end
You may want to have a different conditional on that or whatever... but this is how you bypass validations on a one-off basis.
This may, however, destroy the errors array so you could rebuild it after the save(validate: false) with:
#car.valid?
You can also bypass validations one-at-a-time using #car.update_attribute(:attribute, <value>).
If you just want to know if the model is valid or not without saving it #car.valid? does exactly that. It also adds invalid attributes to errors array. And #pdobb already pointed out how to bypass validation when saving.