开发者

How can I disallow updates except for on one field?

I've been preventing updates to certain models by using this in the model:

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

I'm now writing a plugin that delivers some extra functionality to the model, and I need to update one field in the model. If I weren't using a plugin I would do this directly in the model...

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

I can't do the same from my plugin since I don't know if the开发者_JAVA百科 update behaviour is the ActiveRecord default, or has been overridden to prevent updates. Is there another way to prevent record updates while allowing me to override for a specific field (and only in the instance where my plugin is applied to this model).


First, you should be using a before_update callback for that sort of thing rather than overriding update. Second, you can store the updatable attributes on the model, and then update them with the plugin. I just wrote this in the browser, so it could be wrong.

  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


Late to the game here, but for people viewing this question, you can use attr_readonly to allow writing to a field on create, but not allowing updates.

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

I think it has been available since Rails 2.0

The tricky part is, if you have any attributes that are attr_accessible you have to list your read only attributes there also (or you get a mass assignment error on create):

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


Is this to prevent mass assignment? Would attr_accessible / attr_protected not do what you need?


Edit, just to illustrate the general point about the callback.

 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


I just use the rails params.require method to whitelist attributes that you want to allow.

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜