Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a database field read-only in Rails?

Tags:

I have a database table with a certain field which should be impossible to update once it has been inserted to the database. How do I tell my model that it shouldn't allow updating of a certain field?

like image 406
fresskoma Avatar asked Oct 09 '09 08:10

fresskoma


2 Answers

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 
like image 78
Mike Buckbee Avatar answered Sep 20 '22 17:09

Mike Buckbee


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 
like image 25
stephan.com Avatar answered Sep 23 '22 17:09

stephan.com