Как сделать поле базы данных доступным только для чтения в Rails? - PullRequest
20 голосов
/ 09 октября 2009

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

Ответы [ 3 ]

50 голосов
/ 09 октября 2009

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

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

class Customer < ActiveRecord::Base
    attr_readonly :your_field_name
end
2 голосов
/ 09 октября 2009

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

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

0 голосов
/ 20 апреля 2019

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

  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
...