Подтвердить что ты не робот

Как сделать поле базы данных доступным только для чтения в Rails?

У меня есть таблица базы данных с определенным полем, которое невозможно обновить после того, как оно было вставлено в базу данных. Как сообщить моей модели, что она не должна позволять обновлять определенное поле?

4b9b3361

Ответ 1

Вы хотите использовать attr_readonly:

Атрибуты, перечисленные как доступные только для чтения, будут использоваться для создания новой записи, но операции обновления будут игнорировать эти поля.

class Customer < ActiveRecord::Base
    attr_readonly :your_field_name
end

Ответ 2

И это поле всегда и по определению "правильно" (т.е. точное представление реальности) во время вставки?

Пользователь никогда не ошибается при вводе этого поля для первого (и в вашей схеме: только) времени?

Ответ 3

Здесь мое решение аналогичной проблемы - у нас есть поля, которые мы хотим, чтобы пользователь мог устанавливать самостоятельно, мы не требуем их при регистрации, но мы НЕ хотим, чтобы они были изменены после их установки.

  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

Что меня удивило, так это то, что update_attribute все еще работает, он обходит проверки. Не такая уж большая проблема, поскольку на практике массово обновляются записи, но я это высказал в тестах, чтобы прояснить это. Вот несколько тестов для этого.

    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

      describe '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