2014-04-10 63 views
5

我試圖編輯/使用simple_form更新模型記錄,但窗體不會直接更改模型字段。相反,我提供了幾個check_box_tag字段,告訴更新哪些字段需要更改。因此,更新沒有收到我可以用來更新屬性的params [:device]散列。我試圖創建這個散列,但當我發出@ device.update_attributes(params [:device])時,我得到了ForbiddenAttributesError。ActiveModel :: ForbiddenAttributesError使用update_attributes創建params哈希我自己

我相信我強大的參數列表是正確的。如果我允許在編輯視圖中處理一個模型字段(名稱),我會收到預期的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 %> 

】這個params:我收到的時候我送f.input:姓名,禁用:[設備]假的,允許視圖生成params [:device]是:

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

而且,一切正常。

】這個params [:設備]我創建是:

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

而且,我收到禁止的屬性錯誤,即使我看到了兩者之間沒有什麼區別。

更新爲:< ...代碼重構的需求,一旦工作...>

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中。這從安東·特拉普修復處理強大的參數寶石是尚未完全軌道4兼容:

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) 

不一樣,如果一個模型字段已更新。結果是「param not found:device」。如果沒有模型字段被更新,它可以工作。所以,整個問題都引發了真正錯誤的問題。

回答

1

此修復程序是作爲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 %> 

然後,由於從安東特拉普應用控制器代碼:

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]) 
3

DevicesController#update行動,改變

@device.update_attributes(params[:device]) 

@device.update_attributes(device_params) 

當您使用Rails 4.1,你需要加入白名單,你想插入/更新數據庫中的屬性。當你直接通過屬性update_attributes方法沒有允許他們收到ActiveModel::ForbiddenAttributesError

UPDATE

要解決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 
+0

好的,這個變化使得整體感覺和工作。不過,我仍然懷疑,爲什麼它在涉及模型領域時的方式發揮作用?你知道嗎?謝謝... –

+0

'爲什麼當涉及模型領域的時候它的工作方式?'意味着什麼?哪個模型字段?你的意思是這個代碼在任何情況下工作嗎? –

+0

在編輯視圖中,如果我使用「f.input:name,disabled:false」,則將params [:device]發送到使用params [:davice] [:name]設置的更新操作。在這種情況下,更新操作就像我一樣。相反,如果我使用「f.input:name,disabled:true」,以便名稱字段不能更改,而params [:device]爲nil。爲了update_attributes,我如圖所示構建了params [:device]。兩個哈希都是等價的。但是,即使爲我編寫的編輯視圖使用@ device.update_attributes(params [:device])工作,我在構建的散列上收到了ForbiddenAttributeError。 –

相關問題