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

Я предотвращал обновление определенных моделей, используя это в модели:

def update
  self.errors.add_to_base( "Cannot update a #{ self.to_s }" )
end

Теперь я пишу плагин, который предоставляет дополнительную функциональность модели, и мне нужно обновить одно поле в модели. Если бы я не использовал плагин, я бы сделал это непосредственно в модели...

def update
  if self.changed == ['my_field']
    super
  else
    self.errors.add_to_base( "Cannot update a #{ self.to_s }" )       
  end
end

Я не могу сделать то же самое из своего плагина, так как не знаю, является ли поведение обновления значением ActiveRecord по умолчанию или было переопределено для предотвращения обновлений. Есть ли другой способ предотвратить обновление записей, позволяя мне переопределить для определенного поля (и только в том экземпляре, где мой плагин применяется к этой модели).

Ответ 1

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

  attr_accessor :updatable_attributes
  before_update :prevent_update

  private
  def prevent_update
    return true if self.changed == self.updatable_attributes
    self.errors.add_to_base "Cannot update a #{ self.to_s }"
    false
  end
end

Ответ 2

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

См. http://api.rubyonrails.org/classes/ActiveRecord/ReadonlyAttributes/ClassMethods.html

Я думаю, что он был доступен с Rails 2.0

Сложная часть состоит в том, что если у вас есть какие-либо атрибуты attr_accessible, вы должны также указывать свои атрибуты только для чтения (или вы получаете ошибку массового присваивания при создании):

class Post < ActiveRecord::Base
  attr_readonly :original_title
  attr_accessible :latest_title, :original_title
end

Ответ 3

Это предотвращение массового присвоения? Будет ли attr_accessible/attr_protected не делать то, что вам нужно?


Изменить, просто чтобы проиллюстрировать общий момент обратного вызова.

 module MyModule
   def MyModule.included(base)
     base.send :alias_method_chain, :prevent_update, :exceptions
   end

   def prevent_update_with_exceptions
   end
 end

 class MyModel < ActiveRecord::Base
   before_validation :prevent_update

   def prevent_update
   end

   include MyModule
 end

Ответ 4

Я просто использую метод rails params.require для атрибутов whitelist, которые вы хотите разрешить.

def update
  if @model.update(update_model_params)
    render json: @model, status: :ok
  else
    render json: @model.errors, status: :unprocessable_entity
  end
end

private
  def update_prediction_params
    params.require(:model).permit(:editable_attribute)
  end