【问题标题】:ActiveModel::ForbiddenAttributesError using update_attributes having created params hash myselfActiveModel::ForbiddenAttributesError 使用 update_attributes 自己创建了参数哈希
【发布时间】:2014-05-24 06:45:33
【问题描述】:

我正在尝试使用 simple_form 编辑/更新模型记录,但该表单不会直接更改模型字段。相反,我提供了几个 check_box_tag 字段来告诉更新哪些字段需要更改。结果,更新没有收到我可以用来更新属性的 params[:device] 哈希。我正在尝试创建此哈希,但是当我发出 @device.update_attributes(params[:device]) 时出现 ForbiddenAttributesError。

我相信我的强大参数列表是正确的。如果我允许在编辑视图中处理一个模型字段(名称),我会收到预期的 params[:device] 哈希并且一切正常。如果我禁用该字段,因为我不希望它被更改,那么我需要自己创建该哈希并且我收到错误。当我查看我创建的散列时,在我看来它与视图传递的散列等效。我不明白为什么它失败了。

环境是 Ruby 2.0.0,Windows 8.1 上的 Rails 4.1 和 RubyMine 6.3。

表格是:<...>

<%= simple_form_for @device do |f| %>
      <legend><%= controller.action_name.capitalize %> Device:</legend>
      <%= f.input :name, disabled: true %>
      <%= check_box_tag(:is_admin, 0, @device.admin?) %>
      <%= label_tag(:is_admin, "Make admin?") %>
      <%= check_box_tag(:chg_pwd) %>
      <%= label_tag(:chg_pwd, "Change password?") %>
      <%= f.button :submit %>
<% end %>

我发送 f.input :name, disabled: false 并允许视图生成 params[:device] 时收到的 params[:device] 是:

ActionController::Parameters (3 element(s))
"{"name"=>"D105", "password"=>"D105Dvgr", "password_confirmation"=>"D105Dvgr"}"

而且,一切正常。

我创建的 params[:device] 是:

ActionController::Parameters (3 element(s))
"{"name"=>"D106", "password"=>"D106VdAd", "password_confirmation"=>"D106VdAd"}"

而且,我收到了 Forbidden Attribute Error,即使我看不出两者之间有什么区别。

更新是:<...>

class DevicesController < ApplicationController
  before_filter :authenticate_device!

... other methods removed here ...

  def edit
    @device = Device.find(params[:id])
    # my_page = render_to_string controller: 'devices', action: 'edit', layout: "application"
  end

  def update
    authorize! :update, @device, :message => 'Not authorized as an administrator.'
    @device = Device.find(params[:id])
    pwd_msg = ""
    if params[:chg_pwd]
      pwd_gen = @device.device + SecureRandom.urlsafe_base64(15).tr('lIO0=_\-', 'sxyzEUM').first(4)
      params[:device] = {name: @device.name} if params[:device].nil?
      params[:device][:password] = pwd_gen
      params[:device][:password_confirmation] = pwd_gen
      pwd_msg = ", new password is #{pwd_gen}"
    end
    if @device.update_attributes(params[:device])
      params[:is_admin] ? @device.add_role(:admin) : @device.remove_role(:admin)
      flash[:notice] = ["Device updated" + pwd_msg]
      redirect_to devices_path
    else
      @device.errors.messages.each do |key, value|
        flash[:alert] = ["Unable to update device"]
        @device.errors.messages.each do |key, value|
          flash[:alert] << key.to_s.capitalize + " " + value[0]
        end
      end
      redirect_to devices_path
    end
  end

  private

  def device_params
    params.require(:device).permit(:device, :name, :email, :password, :password_confirmation, :encrypted_password, :salt, :role_ids, :is_admin, :chg_pwd)  # TODO minimize when update is working
  end

end

型号是:

class Device < ActiveRecord::Base

  rolify
  devise :database_authenticatable, :rememberable, :trackable, :validatable

  validates :device,
            presence: true,
            length: {minimum: 4 },
            uniqueness: {case_sensitive: false }
  validates :name,
            presence: true

  def remember_me
    true unless self.admin?
  end

  def admin
    self.add_role :admin
  end

  def not_admin
    self.remove_role :admin
  end

  def admin?
    self.has_role? :admin
  end
  def device?
    self.has_role? :device
  end
  def vip?
    self.has_role? :vip
  end

  def login=(login)
    @login = login
  end
  def login
    @login || self.device || self.email
  end

  def self.find_first_by_auth_conditions(warden_conditions)
    conditions = warden_conditions.dup
    if login = conditions.delete(:login)  # Note one equal sign.  Strange but true.
      where(conditions).where(["lower(device) = :value OR lower(email) = :value", { :value => login.downcase }]).first
    else
      where(conditions).first
    end
  end

end

新信息:我忽略了提供我在 ApplicationController 中的信息。来自 Anton Trapp 的此修复处理尚未完全兼容 Rails 4 的 gem 的强参数:

  before_filter do
    resource = controller_name.singularize.to_sym
    method = "#{resource}_params"
    params[resource] &&= send(method) if respond_to?(method, true)
  end

我发现使用以下建议的解决方案:

@device.update_attributes(device_params)

如果模型字段被更新,则不起作用。结果是“找不到参数:设备”。如果没有更新模型字段,它确实有效。所以,整个问题都引出了真正错误的问题。

【问题讨论】:

    标签: ruby-on-rails ruby hash ruby-on-rails-4 params


    【解决方案1】:

    在DevicesController#update 操作中,更改

    @device.update_attributes(params[:device])
    

    到

    @device.update_attributes(device_params)
    

    当您使用Rails 4.1 时,您需要将您希望在数据库中插入/更新的属性列入白名单。当您将属性直接传递给update_attributes 方法而不允许它们时,您收到了ActiveModel::ForbiddenAttributesError

    更新

    解决param not found: device:

      def device_params
        if params[:device]
          params.require(:device).permit(:device, :name, :email, :password, :password_confirmation, :encrypted_password, :salt, :role_ids, :is_admin, :chg_pwd)  # TODO minimize when update is working 
        end
      end
    

    【讨论】:

    • 好的,这个改变完全有意义并且有效。不过,我仍然想知道,为什么它会像涉及模型领域时那样工作?你知道吗?谢谢...
    • why it worked the way it was when a model field was involved? 是什么意思?哪个型号领域?你的意思是同样的代码在任何情况下都有效吗?
    • 在编辑视图中,如果我使用“f.input :name, disabled: false”,那么 params[:device] 会通过 params[:davice][:name] 发送到更新操作放。在那种情况下,更新操作就像我一样工作。相反,如果我使用“f.input :name, disabled: true”,以便无法更改名称字段,则 params[:device] 为 nil。为了更新属性,我构建了 params[:device],如图所示。两个哈希值是等价的。但是,我在我构建的哈希上收到了 ForbiddenAttributeError,即使为我构建的编辑视图使用 @device.update_attributes(params[:device]) 工作。
    • 您的意思是说,当您输入一个新的设备名称 并提交表单时,名称字段上有disabled: false 选项。设备名称更新成功且没有ActiveModel::ForbiddenAttributesError 错误?
    • 没有。我正在编辑现有设备。我不想改名。所以,我想要禁用:真的。但是,这意味着没有模型字段改变,所以没有 params[:device] 被传递。如果我设置 disabled: false,params[:device][:name] 不管它是否改变都会被传递,整个事情就像我编码的那样工作。
    【解决方案2】:

    解决方法是将字段作为 attr_accessor 添加到模型中,而不是数据库中,以便它可以在表单中正确使用。

      attr_accessor :is_admin, :chg_pwd
    

    然后将视图修改为:

    <%= simple_form_for @device do |f| %>
        <legend><%= controller.action_name.capitalize %> Device:</legend>
        <%= f.input :name, disabled: true %>
        <%= f.input :is_admin, as: :boolean, checked_value: true, unchecked_value: false %>
        <%= f.input :chg_pwd, as: :boolean, checked_value: true, unchecked_value: false %>
        <%= f.button :submit %>
    <% end %>
    

    然后,由于来自 Anton Trapp 的应用程序控制器代码:

      before_filter do
        resource = controller_name.singularize.to_sym
        method = "#{resource}_params"
        params[resource] &&= send(method) if respond_to?(method, true)
      end
    

    我能够按如下方式更新设备控制器中的字段:

    @device.update_attributes(params[:device])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多