Is it possible to validate a field for presence after the initial creation?
I want to make phone number mandatory if the user wants to update their account after signing up.
validates :phone, presence: true, if: .....
if I use on: :update I can no longer authenticate until the field is filled
There are many ways to accomplish this task assuming it is a normal Rails model backed by a DB table. Off the top of my head you can do:
validates :phone,
presence: true,
if: Proc.new{ |model| model.id.present? }
Or more to the point and doesn't fail if you assign an ID before saving:
validates :phone,
presence: true,
if: Proc.new{ |model| model.persisted? }
In my user model I have a validation rule like:
validates :email, presence: true, my_email_format: true, uniqueness: true
I want to break the chain when any validation is failed, such as when the email format is wrong (my_email_format failed), the uniqueness validation will not run.
I'm not sure why you want that but if you want to, you can split the validates into multiple lines
validates :email, presence: true
validates :email, my_email_format: true, if: ->{errors[:email].blank?}
validates :email, uniqueness: true, if: ->{errors[:email].blank?}
I would suggest you to create before_validation hook. Throw :abort message when you want to break the validation chain.
For example:
before_validation :validate_email, on: :create
...
def validate_email
if (email_is_invalid)
errors.add(:base, error_message)
throw(:abort)
end
end
I just came across a situation where the accepted solution for Validate presence of field only if another field is blank - Rails wasn't complete.
I'd like one or both of two fields to be present but in my case one of the fields points needs to be a number between 0 and 10. In cases where the points field is zero :points? is evaluating to false and a blank comment is considered invalid. I tried to make it a little more specific by specifying :points.blank?:
validates :comment, presence: { unless: :points.blank?, on: :create }
validates :points, presence: { unless: :comment?, on: :create },
numericality: { only_integer: true, allow_nil: true, less_than_or_equal_to: 10, greater_than_or_equal_to: 0 }
That seems to work (I can save the first entry with 0 points and no comment) but on subsequent saves I'm getting weird errors from the controller:
NoMethodError (undefined method 'validate' for false:FalseClass):
app/controllers/comments_controller.rb:8:in 'create'
Is there something else I need to do with the validation? Do I need to use a lambda (as suggested in some answers to the linked question)?
:points.blank? evaluates to false, which is being set as the value of the :unless key in your hash. You will need to specify a method name or Proc for the :unless option to work. You can simplify the whole setup:
validates :comment, presence: true, on: :create, if: :points?
validates :points, numericality: :only_integer, inclusion: 0..10
Not sure how you got first record working, but this is not correct syntax:
validates :comment, presence: { unless: :points.blank?, on: :create }
Instead you should be defining a method like this in your model:
def points_blank?
# your code for zero handling
#
end
and then use that method in unless like this:
validates :comment, presence: { unless: :points_blank?, on: :create }
I have 2 forms.
Form 1 I have 10 fields which I am validating.
Form 2 but it only contains 2 fields.
The model is same.
What I need to do is:
To validate fields when they are submitted by forms. If I am not posting any fields it should not validate in model. If I post 5 fields it should validate 5 fields. If I post 2 fields it should validate only 2 not all of them.
So form 1 all 10 should be validated, form 2 only 2 should validate not rest of 8.
Here is my code:
validates :teacher_number, :title, :name, :gender, :location, :dob,
:contact_mobile, :contact_home, :street, :city, :state, :zip_code, :country,
presence: true
validates :teacher_number, uniqueness: {scope: :school_id}
validate :teacher_number_existance, :on => :create
validate :school_existance, :on => :create
Below is my attempt which successfully works fine but its bulk of code that somewhat a bad practice.
validates :teacher_number, presence: true, if: "teacher_number && teacher_number.blank?"
validates :title, presence: true, if: "title && title.blank?"
validates :name, presence: true, if: "name && name.blank?"
validates :gender, presence: true, if: "gender && gender.blank?"
validates :location, presence: true, if: "location && location.blank?"
validates :dob, presence: true, if: "dob && dob.blank?"
validates :contact_mobile, presence: true, if: "contact_mobile && contact_mobile.blank?"
validates :contact_home, presence: true, if: "contact_home && contact_home.blank?"
validates :street, presence: true, if: "street && street.blank?"
validates :city, presence: true, if: "city && city.blank?"
validates :state, presence: true, if: "state && state.blank?"
validates :zip_code, presence: true, if: "zip_code && zip_code.blank?"
validates :country, presence: true, if: "country && country.blank?"
validates :teacher_number, uniqueness: {scope: :school_id}, if: "teacher_number && teacher_number.blank?"
validate :teacher_number_existance, :on => :create, if: "self.teacher_number && self.teacher_number.blank?"
validate :school_existance, :on => :create, if: "self.teacher_number && self.teacher_number.blank?"
EDIT
UPDATED MY QUESTION.
I see two ways for this:
Some hidden param in form and attr_accesor in model to turn off validation.
Use save(validate: false) for save from that second form.
Next, you can do it like this
if validate_object?
validates :email, presence: true
validates :variant, presence: true
end
You can use some patterns like form object.
But you have to remember that this object will be invalid in future too.
If you want different validations of data in different circumstances, you should not be validating on the model. You should validate elsewhere: either in the view using HTML form validation, or in the controller using Rails' Strong Params.
I think you should use HTML form validation to manage this, because you're worried about the record missing attributes. Strong Params is more useful in case you are worried about people supplying forbidden values for record attributes.
Here is how you would use HTML form validation to manage this (example using HAML):
= form_for #model do |f|
= f.text_input(:teacher_number, required: true)
...all your other inputs...
= f.submit 'Submit Form'
Here is how you would use Strong Params to constrain the number of things you can get:
class ModelsController < ApplicationController
def form_action_1
Model.create(form_1_params)
end
def form_action_2
Model.create(form_2_params)
end
private
def form_1_params
# let's permit all the things you want for the first form
params.require(:model).permit(:teacher_number, :title, ...)
end
def form_2_params
# let's permit only the few things you want in the second form
params.require(:model).permit(:only, :a, :few, :things)
end
end
I'm not sure exactly what you're asking, but perhaps this approach would work. Write your validations like this:
validates :teacher_number,
:title,
:name,
:gender,
:location,
:dob,
:contact_mobile,
:contact_home,
:street,
:city,
:state,
:zip_code,
:country,
presence: true,
on: :form2
validates :teacher_number, uniqueness: {scope: :school_id}
validate :teacher_number_existance, :on => :create
validate :school_existance, :on => :create
The on: :form2 near the bottom of the first validator means the validations will only run if you explicitly ask them to run.
So in your controller action for saving form2, you would have to do this:
if valid?(:form2) && #model.save
The other validators will run when you save, as normal. Using the on: parameter of validates for your own symbols (as opposed to the built-in ones) is covered in the Rails Guide for validations, but it's easy to miss. I didn't notice it myself until recently. I hope this helps.
Finally, after so going back and forth below solution worked well for me.
before_validation :strip_validations
def strip_validations
['teacher_number', 'title', 'name', 'gender', 'location', 'dob', 'contact_mobile', 'contact_home', 'street', 'city', 'state', 'zip_code', 'country'].each do |attr|
errors.add("Teacher", {attr => " #{attr} can't be blank"}) if send(attr.to_sym) && send(attr.to_sym).blank?
end
end
I have now this validate for my User model:
validates :email,
presence: true,
uniqueness: { :case_sensitive => false }
I'd like to add :on create for the uniqueness as users are definitely allowed to update their email by putting the same email!
Should I write it this way? I'm afraid the on:create also applies to the presence:true but it should only apply to the uniqueness validation:
validates :email,
presence: true,
uniqueness: { :case_sensitive => false }, on: :create
I would like to say some logic. Email should be unique and user will be identified by his email. So while update, there is no need to put email field where user can edit the email value. You can make the email field as readonly, so that user can not change it while updating the profile.
And yes, the syntax on: :create is the nice solution for it.
yes it will applied on both you can use separate validation for that
validates :email, presence: true
validates :email, uniqueness: { :case_sensitive => false }, on: :create