'rails 4 before_validation on: :create or on: :save
I am having a case which is getting around my head.
I have an Image model which I only want to save if it gets uploaded. I also need some information coming from the upload to validate the image(like height and width). But I want only the upload to happen if somebody is trying to save the file the image for the first time.
So I thought the best option would be to have a before_validation, but I would like it to run only on save!
My code is on this gist https://gist.github.com/andreorvalho/b21204977d2b70fdef83
So the weird part is this on: :save and on: :create have really weird behaviours or at least not what I expected.
When I put it as on: :save If I try to do an image.save on a test I can see my before_validation callbacks are not ran!
If I put on: :create is ran in every situation is does not matter if I ran image.save, image.create or image.valid?
So I am guessing this is either not working or I am misunderstanding the goal of those on settings.
p.s. my validation on create, also occurs in every situation save, create or valid?
let me know if anybody ran into the same or knows why is not supposed to work like this.
Solution 1:[1]
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: :saveIf I try to do animage.saveon a test I can see mybefore_validationcallbacks 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: :createis ran in every situation is does not matter if I ranimage.save,image.createorimage.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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | radiantshaw |
