Reputation: 626
I want to make an attribute read only for each record after the first update, is there a way to do this in Rails 4 using attr_readonly? Or another method?
It has to do with Transaction security...
Upvotes: 2
Views: 2636
Reputation: 106077
attr_readonly
is a class method, so it would "freeze" the attribute on all instances of the model, whether they've been updated or not.
The sanest way to do what you want, I think, would be to add a some_attribute_is_frozen
boolean attribute to your model, and then set it to true
in a before_update
callback. Then you can have a validation that will only run if some_attribute_is_frozen?
is true
, and which will fail if the "frozen" attribute has changed.
Something like this (for the sake of an example I've arbitrarily chosen "Customer" as the name of the model and address
as the name of the attribute you want to "freeze"):
# $ rails generate migration AddAddressIsFrozenToCustomers
# => db/migrate/2014XXXX_add_address_is_frozen_to_customers.rb
class AddAddressIsFrozenToCustomers < ActiveRecord::Migration
def change
add_column :customers, :address_is_frozen, :boolean,
null: false, default: false
end
end
# app/models/customer.rb
class Customer < ActiveRecord::Base
before_update :mark_address_as_frozen
validate :frozen_address_cannot_be_changed, if: :address_is_frozen?
# ...snip...
private
def mark_address_as_frozen
self.address_is_frozen = true
end
def frozen_address_cannot_be_changed
return unless address_changed?
errors.add :address, "is frozen and cannot be changed"
end
end
Since the before_update
callback runs after validation, the very first time the record is updated, address_is_frozen?
will return false
and the validation will be skipped. On the next update, though, address_is_frozen?
will return true
and so the validation will run, and if address
has changed the validation will fail with a useful error message.
I hope that's helpful!
Upvotes: 3