-
Notifications
You must be signed in to change notification settings - Fork 9
Open
Description
The case where it got me is somewhat convoluted (though real), yet I believe that it exposes some conceptual problem.
The case is this:
class Rate < ApplicationModel
# columns are: rate_type, rate_value, rate_currency, etc.
str_enum :rate_type, %w[fixed hourly], allow_nil: true, default: nil
attr_readonly :rate_type
endSince Rails 7.1, the default behavior of attr_readonly is to raise when #rate_type= is used on already persisted model. And that's where str_enum introduces a problem:
rate = Rate.create!(rate_type: nil, rate_value: 10)
Rate.find_by(rate.id) # ActiveRecord::ReadonlyAttributeError: rate_typeThe culprit is here:
after_initialize do
send("#{column}=", default_value) if has_attribute?(column) && !try(column)
endWhat we have here is:
after_initializeis triggered after the model is loaded alsorate_typeisnil(as loaded from DB), thus!try(:rate_type)istrue- thus,
str_enumtries to doself.rate_type = nil(which would be pointless anyway), triggering the "it tries to write to readonly column".
I believe the most comprehensive fix is to NOT set anything in after_initialize if it is an initialization of a model loaded from DB (i.e. if persisted?). As far as I understand, the intention of this block is that when the new record is initialized, the default should be set.
Or I might be missing something.
Metadata
Metadata
Assignees
Labels
No labels