How to make a database field read-only in Rails?
Here's my related solution to a similar problem - we have fields that we want a user to be able to set themselves, we don't require them on creation of the record, but we do NOT want to them be changed once they are set.
validate :forbid_changing_some_field, on: :update
def forbid_changing_some_field
return unless some_field_changed?
return if some_field_was.nil?
self.some_field = some_field_was
errors.add(:some_field, 'can not be changed!')
end
The thing that surprised me, though, was that update_attribute
still works, it bypasses the validations. Not a huge deal, since updates to the record are mass assigned in practice - but I called that out in the tests to make it clear. Here's some tests for it.
describe 'forbids changing some field once set' do
let(:initial_some_field) { 'initial some field value' }
it 'defaults as nil' do
expect(record.some_field).to be nil
end
it 'can be set' do
expect {
record.update_attribute(:some_field, initial_some_field)
}.to change {
record.some_field
}.from(nil).to(initial_some_field)
end
context 'once it is set' do
before do
record.update_attribute(:some_field, initial_some_field)
end
it 'makes the record invalid if changed' do
record.some_field = 'new value'
expect(record).not_to be_valid
end
it 'does not change in mass update' do
expect {
record.update_attributes(some_field: 'new value')
}.not_to change {
record.some_field
}.from(initial_some_field)
end
it 'DOES change in update_attribute!! (skips validations' do
expect {
record.update_attribute(:some_field, 'other new value')
}.to change {
record.some_field
}.from(initial_some_field).to('other new value')
end
end
end
You want to use attr_readonly
:
Attributes listed as readonly will be used to create a new record but update operations will ignore these fields.
class Customer < ActiveRecord::Base
attr_readonly :your_field_name
end