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

Есть ли простой способ сделать модель Rails ActiveRecord доступной только для чтения?

Я хочу иметь возможность создать запись в БД, а затем не позволять Rails делать изменения с этой точки. Я понимаю, что изменения будут по-прежнему возможны на уровне БД.

Я верю, что attr_readonly делает то, что я хочу, на уровне атрибута, но я не хочу вручную указывать поля... Я предпочел бы иметь более белый подход.

Кроме того, я знаю, что для ассоциаций есть опция: read_only, но я не хочу ограничивать "readonlyness" объекта, если он был получен через ассоциацию или нет.

Наконец, я хочу, чтобы все еще можно было уничтожить запись, поэтому такие вещи, как: dependent = > : destroy, работают в ассоциациях.

Итак, суммируем: 1) разрешаем создание записей, 2) разрешаем удаление записей и 3) предотвращаем изменение сохраненных записей.

4b9b3361

Ответ 1

Глядя на ActiveRecord::Persistence, все заканчивается вызовом create_or_update за кулисами.

def create_or_update
  raise ReadOnlyRecord if readonly?
  result = new_record? ? create : update
  result != false
end

Итак! Просто:

def readonly?
  !new_record?
end

Ответ 2

Я нашел более сжатое решение, которое использует обратный вызов after_initialize:

class Post < ActiveRecord::Base
  after_initialize :readonly!
end

Ответ 3

Почему бы просто не создать пользователя в базе данных, имеющей доступ только для чтения, и использовать рельсы для этой учетной записи.

Однако, если вам нужен доступ к уровню модели, вы можете добавить следующее к определенной модели:

 def readonly?
    true
  end

  def before_destroy
    raise ActiveRecord::ReadOnlyRecord
  end

Ответ 4

Этот пост в блоге по-прежнему действителен: http://ariejan.net/2008/08/17/activerecord-read-only-models/

В основном вы можете положиться на проверку ActiveRecord, если добавить метод:

def readonly?
  true
end

Ответ 5

TL; DR для OP

class YourModel < ActiveRecord::Base
  before_save { false } # prevent create & update, allows destroy

  # ... 
end

Вообще

  • Чтобы предотвратить создание только: before_create { false }
  • Только для предотвращения обновлений: before_update { false }
  • Чтобы предотвратить только уничтожение: before_destroy { false } # does not prevent delete

Смотрите также: http://guides.rubyonrails.org/active_record_callbacks.html

Ответ 6

Это кажется довольно эффективным и, вероятно, немного переборщием, но для моего случая я действительно хочу быть уверенным, что мое приложение никогда не будет создавать, сохранять, обновлять или уничтожать любые записи в модели, когда-либо.

module ReadOnlyModel
  def readonly?() true end
  def create_or_update() raise ActiveRecord::ReadOnlyRecord end
  before_create { raise ActiveRecord::ReadOnlyRecord }
  before_destroy { raise ActiveRecord::ReadOnlyRecord }
  before_save { raise ActiveRecord::ReadOnlyRecord }
  before_update { raise ActiveRecord::ReadOnlyRecord }
end

class MyModel < ActiveRecord::Base
  include ReadOnlyModel
  # ...
end

Поскольку OP попросил, чтобы иметь возможность создавать и уничтожать, но не сохранять или обновлять, я считаю, что это сработает

module SaveAndDestroyOnlyModel
  before_save { raise ActiveRecord::ReadOnlyRecord }
  before_update { raise ActiveRecord::ReadOnlyRecord }
end

class MyModel < ActiveRecord::Base
  include SaveAndDestroyOnlyModel
  # ...
end

Не совсем правильное исключение, но достаточно близко, я думаю.

Ответ 7

Пользовательский валидатор может сделать это:

validate :nothing_changed, unless: :new_record? # make immutable

...

def nothing_changed
  errors.add(:base, "Record is read-only") if self.changed?
end

Ответ 8

В поисках способа достижения того же элемента управления, предложенного @Nate (избегая любого вида создания/обновления/удаления), но используя его только в определенных частях моего приложения и для всех моделей одновременно, я создал это уточнение Ruby:

module ReadOnlyRailsMode
  CLASS_METHODS    = ActiveRecord::Base.methods
    .select { |m| m =~ /(update|create|destroy|delete|save)[^\?]*$/ }

  INSTANCE_METHODS = ActiveRecord::Base.instance_methods
    .select { |m| m =~ /(update|create|destroy|delete|save)[^\?]*$/ }

  refine ActiveRecord::Base.singleton_class do
    CLASS_METHODS.each do |m|
      define_method(m) do |*args|
        raise ActiveRecord::ReadOnlyRecord
      end
    end
  end

  refine ActiveRecord::Base do
    def readonly?; true; end

    INSTANCE_METHODS.each do |m|
      define_method(m) do |*args|
        raise ActiveRecord::ReadOnlyRecord
      end
    end
  end
end

И использовать его только в определенной части кода:

class MyCoolMailerPreview < ActionMailer::Preview
  using ReadOnlyRailsMode 
end

(Это реальный случай использования, я искал способ избежать людей, создающих и редактирующих реальные записи из ActionMailer :: Previews, потому что я хочу разрешить предварительный просмотр в производстве, но если по ошибке кто-то создает предварительный просмотр, который изменяет реальные данные, это стало бы хаосом).

Код немного некрасиво переопределяет все методы (create, create !, и т.д.), Потому что цель состоит в том, чтобы изменить поведение всех моделей, и обратные вызовы типа "before_create" не могут использоваться для этой цели, так как они не будут только локально в область "использования", изменяя все приложение.

Этот подход работает для меня, я могу явно заблокировать все эти методы для всех моделей только в одном классе и не связываться с остальной частью приложения. К сожалению, до сих пор уточнения не применялись к подклассам, поэтому в моем случае я не смог заблокировать все вставки по умолчанию в родительский класс (ActionMailer :: Preview), что было моей первоначальной целью, но блокировка для каждого класса хорошая отправная точка.

Мое приложение требует уточнения всех методов, но контроль может быть выполнен только для интересных методов, таких как уничтожение или обновление, и они могут работать во всех случаях, в том числе из исходного вопроса.