rails 4 before_validation on: :create or on: :save
The before_validation
callback allows restrictions to particular events within the lifecycle of an object.
You can use the following syntax:
before_validation :set_uuid, on: :create
before_validation :upload_to_system, on: [:create, :update]
Here is the documentation of this option for Rails 4.0, Rails 5.2, Rails 6.0 and Rails 7.0.
I came across the same issue and here's what I found.
#valid?
is a method which also accepts an optional parameter called context
(which for some reason a lot of people don't know about, including me before I stumbled upon this question and did some research). Passing in the context
when the #valid?
method is called will result in only those before_validation
callbacks being run for which the same context
is set using the :on
key.
Example
Let's say we have the following code:
class Model < ActiveRecord::Base
before_validation :some_method, on: :create
before_validation :another_method, on: :update
before_validation :yet_another_method, on: :save
before_validation :also_another_method, on: :custom
end
Now, calling:
Model.new.valid?(:create)
will only run :some_method
. Likewise calling:
Model.new.valid?(:update)
Model.new.valid?(:save)
Model.new.valid?(:custom)
will only run :another_method
, :yet_another_method
, and :also_another_method
respectively. But, if we do:
Model.new.valid?(:unknown)
then it will not call any callbacks because we did not specify :unknown
as a context while creating callbacks.
Also, one other thing to note is that if we do not pass a context
when calling #valid?
, then ActiveRecord will internally use the new_record?
method to figure it out. That is, if new_record?
returns true
, then the context
will be set to :create
, but if it returns false
, then the context
will be set to :update
.
Coming back to your question
When I put it as
on: :save
If I try to do animage.save
on a test I can see mybefore_validation
callbacks are not ran!
That's because ActiveRecord's #save
method internally calls #valid?
without passing an explicit context
, which means now the #valid?
method will have to decide whether to use :create
or :update
as a context
based on the boolean returned by #new_record?
. And since you've specified on: :save
, it doesn't run. Yes, that's right, :save
context doesn't exist internally in ActiveRecord.
If I put
on: :create
is ran in every situation is does not matter if I ranimage.save
,image.create
orimage.valid?
This is true, but only if image
is a new record. Try doing the same for an existing record and it will not run.