40

I am using Rails v2.3

If I have a model:

class car < ActiveRecord::Base

  validate :method_1, :method_2, :method_3

  ...
  # custom validation methods
  def method_1
    ...
  end

  def method_2
    ...
  end

  def method_3
    ...
  end
end

As you see above, I have 3 custom validation methods, and I use them for model validation.

If I have another method in this model class which save an new instance of the model like following:

# "flag" here is NOT a DB based attribute
def save_special_car flag
   new_car=Car.new(...)

   new_car.save #how to skip validation method_2 if flag==true
end

I would like to skip the validation of method_2 in this particular method for saving new car, how to skip the certain validation method?

Leem.fin
  • 40,781
  • 83
  • 202
  • 354
  • Does your 'special car' have any attribute that can differentiate it from non-special cars? That would make this simple. – Dogbert Jan 16 '12 at 15:00
  • 1
    Have you gone over the [conditional validation](http://guides.rubyonrails.org/active_record_validations_callbacks.html#conditional-validation) docs? – Dave Newton Jan 16 '12 at 15:00
  • Hi, I updated my post for the method to save a special car. Basically, i have a "flag" parameter pass to the method, and if "flag" is true, I would like to skip the validation of method_2 – Leem.fin Jan 16 '12 at 15:02
  • @ Dave, I have gone over that. But what I want to achieve is in a method level, I am wondering is there any possibility to do it in method level, and how. – Leem.fin Jan 16 '12 at 15:03

6 Answers6

74

Update your model to this

class Car < ActiveRecord::Base

  # depending on how you deal with mass-assignment
  # protection in newer Rails versions,
  # you might want to uncomment this line
  # 
  # attr_accessible :skip_method_2

  attr_accessor :skip_method_2 

  validate :method_1, :method_3
  validate :method_2, unless: :skip_method_2

  private # encapsulation is cool, so we are cool

    # custom validation methods
    def method_1
      # ...
    end

    def method_2
      # ...
    end

    def method_3
      # ...
    end
end

Then in your controller put:

def save_special_car
   new_car=Car.new(skip_method_2: true)
   new_car.save
end

If you're getting :flag via params variable in your controller, you can use

def save_special_car
   new_car=Car.new(skip_method_2: params[:flag].present?)
   new_car.save
end
shime
  • 8,746
  • 1
  • 30
  • 51
  • That `attr_writer` should be `attr_accessor` or you'll get an `undefined local variable or method 'skip_method'` error. Or you could write your own reader function like `skip_method?`. – Ashitaka Jun 16 '13 at 21:08
  • Thanks for this answer ..I searched a lot for this.For anyone else who might come across this ,I think you'll get a MassAssignment error if you do it like that . You can avoid the error by doing like so:`new_car= Car.new` , `new_car.skip_method_2= params[:flag].present?` – simha Jul 13 '13 at 15:51
  • @lnreddy thanks for pointing out the mass assignment error. I've updated the answer to newer Rails versions. – shime Jul 18 '13 at 10:34
18

The basic usage of conditional validation is:

class Car < ActiveRecord::Base

  validate :method_1
  validate :method_2, :if => :perform_validation?
  validate :method_3, :unless => :skip_validation?

  def perform_validation?
    # check some condition
  end

  def skip_validation?
    # check some condition
  end

  # ... actual validation methods omitted
end

Check out the docs for more details.

Adjusting it to your screnario:

class Car < ActiveRecord::Base

  validate :method_1, :method_3
  validate :method_2, :unless => :flag?

  attr_accessor :flag

  def flag?
    @flag
  end    

  # ... actual validation methods omitted
end

car = Car.new(...)
car.flag = true
car.save
Michał Szajbe
  • 8,830
  • 3
  • 33
  • 39
1

Another technique, which applies more to a migration script than application code, is to redefine the validation method to not do anything:

def save_special_car
   new_car=Car.new
   new_car.define_singleton_method(:method_2) {}
   new_car.save
end

#method_2 is now redefined to do nothing on the instance new_car.

fkoessler
  • 6,932
  • 11
  • 60
  • 92
0

Use block in your validation something like :

validates_presence_of :your_field, :if =>  lambda{|e| e.your_flag ...your condition}
Ravindra
  • 1,039
  • 2
  • 12
  • 26
0

With rails 6.x+ I think you can call .save or .valid? with a context attribute, like .save(:a_context) and, on the model side, use a validation like:

validate :method_2, on: :a_context

Ref: https://guides.rubyonrails.org/active_record_validations.html#on

Ccastillop
  • 791
  • 6
  • 9
-5

Depending on weather flag is true of false, use the method save(false) to skip validation.

Wahaj Ali
  • 4,093
  • 3
  • 23
  • 35