I am trying to assign a random number to an attribute named position
before my Product
objects are saved. I cannot get the callback to work properly. There are no errors in the console, it just silently fails.
Here is my model
class Product < ApplicationRecord
validates_presence_of :position
before_save {
assign_position
}
private
def assign_position
self.position = rand(150)
end
end
This is my schema
create_table "products", force: :cascade do |t|
t.string "name"
t.integer "position"
end
Here is an example seed I'm using to test the before save callback.
Product.create(name: "Rose Deep Hydration Facial Toner")
There are no errors but the console output when I check after creating the seed
irb(main):079:0> Product.find_by_name("Rose Deep Hydration Facial Toner").position
Product Load (1.8ms) SELECT "products".* FROM "products" WHERE "products"."name" = $1 ORDER BY "products"."created_at" DESC LIMIT $2 [["name", "Rose Deep Hydration Facial Toner"], ["LIMIT", 1]]
=> nil
I have other callbacks in the application that work just fine. I can't figure out why this one is not working.
CodePudding user response:
Your problem is that validations happen before before_save
callbacks are triggered. You have this:
validates_presence_of :position
but position
will still be nil
when that validation is checked.
I think you want to use a before_validation
callback instead of before_save
:
class Product < ApplicationRecord
before_validation :assign_position
validates_presence_of :position
private
def assign_position
self.position = rand(150)
end
end
Keep in mind that assign_position
will get called every time you validate a Product so updating a typo in the name (for example) will change the position
. You might want to leave the position alone if it is already set or if you're altering an existing instance.