【问题标题】:Ruby on Rails: ActiveModel::ForbiddenAttributesError with strong paramsRuby on Rails:具有强参数的 ActiveModel::ForbiddenAttributesError
【发布时间】:2017-03-03 09:09:45
【问题描述】:

在我的 RoR 应用程序中,我有一个 update_multiple 方法,它使用用户的输入更新多条记录。但是,由于某种原因,尽管使用了强大的参数,但我得到了错误 ActiveModel::ForbiddenAttributesError。有人可以帮我解决这个问题吗?

Recipients_Controller 中的update_multiple 方法如下:

def update_multiple
    @email = Email.find_by_id(params[:email_id])
    if Recipient.update(params[:recipient].keys, params[:recipient].values)
        @listofcontacts = Recipient.where("id in (?)", params[:recipient].keys)
        @account = Account.find_by_id(@email.account_id)
        @listofcontacts.each do |f|
            recipient_message = @email.message
            recipient_message = recipient_message.gsub("VAR1", f.var1)
            contact = Contact.find_by_id(f.contact_id)
            @unsubscribe = Rails.application.message_verifier(:unsubscribe).generate(contact.id)
            UserEmails.send_email(@email, @account, contact.email, @unsubscribe, recipient_message).deliver_now
        end
        flash[:notice] = "recipients were updated"
        redirect_to root_path
    else
        render 'edit_multiple'
    end
end

private
def recipient_params
  params.require(:recipient).permit(:contact_id, :group_id, :email_id, :var1, :var2, :var3)
end

此方法从该表单获取用户输入:

<%= form_for :recipient, :url => update_multiple_recipients_path, :html => { :method => :put }  do %>
    <fieldset>
        <table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered" id="example">
            <thead>
            <tr>
                <th>Contact</th>
                <% if @email_message.upcase.include? "VAR1" %><th>VAR1</th><% end %>
            </tr>
            </thead>
            <tbody>
                <%= hidden_field_tag :email_id, @email %>
                <% @recipients.each do |recipient| %>
                    <tr class="odd gradeX">
                        <%= fields_for "recipient[]", recipient do |recipient_fields| %>
                        <td><%= recipient_fields.label recipient.contact.firstname %> <%= recipient_fields.label recipient.contact.surname %></td>
                        <% if @email_message.upcase.include? "VAR1" %><td><%= recipient_fields.text_field :var1, :required => true, :maxlength => 20 %></td><% end %>
                        <% end %>
                    </tr>
                <% end %>
            </tbody>
        </table></br>
        <%= submit_tag 'Send Email', {:class => 'btn btn-primary'} %></br>
        <%= link_to "Back", edit_email_path(@email) %>
    </fieldset> 
<% end %>

development.log 是这样写的:

Started PUT "/recipients/update_multiple" for ::1 at 2017-03-03 09:33:10 +0000
Processing by RecipientsController#update_multiple as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"BJtQ56CW169tJ0Yqlc7BZNZk8SiTCauvkpNkXRUqVv4WESSS/DGFVDe3uQnfTxxDgif8lbg8THtmxHT9bOh0zw==", "email_id"=>"292", "recipient"=>{"635"=>{"var1"=>"ben"}}, "commit"=>"Send Email"}
  [1m[36mEmail Load (0.0ms)[0m  [1mSELECT  "emails".* FROM "emails" WHERE "emails"."id" = ? LIMIT 1[0m  [["id", 292]]
  [1m[35mRecipient Load (1.0ms)[0m  SELECT  "recipients".* FROM "recipients" WHERE "recipients"."id" = ? LIMIT 1  [["id", 635]]
  [1m[36m (0.0ms)[0m  [1mbegin transaction[0m
  [1m[35m (0.0ms)[0m  rollback transaction
Completed 500 Internal Server Error in 5ms (ActiveRecord: 1.0ms)

ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
  app/controllers/recipients_controller.rb:15:in `update_multiple'


  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (4.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (1459.1ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/_markup.html.erb (0.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string (0.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string (0.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/style.css.erb within layouts/inlined_string (0.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/console.js.erb within layouts/javascript (1449.1ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/main.js.erb within layouts/javascript (0.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/error_page.js.erb within layouts/javascript (0.0ms)
  Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/index.html.erb (3305.2ms)

上面写着app/controllers/recipients_controller.rb:15:in 'update_multiple',它指向if Recipient.update(params[:recipient].keys, params[:recipient].values)这一行

我真的无法弄清楚为什么我会收到此错误。有人可以帮帮我吗?

我查看了其他各种 SO 问题,它们似乎已由 strong_params 解决,但我的已声明 strong_params 并且无法正常工作。

【问题讨论】:

  • 我可以帮助你,但我需要一些额外的信息:首先,我在任何地方都看不到对 recipient_params 的调用,其目的是过滤来自表单的内容。因为它没有被称为它,所以不能犯那个错误......其次,你能在日志中看到ActiveModel::ForbiddenAttributesError 的一小块吗?也许我们可以找到一些有助于理解它的来源
  • 你没有在任何地方使用recipient_params,仅仅声明它是不够的
  • 感谢您的cmets,我已经用日志更新了问题。 @Iceman,我将如何使用 recipient_params 来更新多个?

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-4 strong-parameters


【解决方案1】:

问题在于这一行:

if Recipient.update(params[:recipient].keys, params[:recipient].values)

您将params 直接传递给update 方法。您需要将recipient_params 传递给update

if Recipient.update(recipient_params.keys, recipient_params.values)

更新

但是,从您的日志中,很明显 params[:recipient] 正在返回一个包含 id/attribute 对的哈希,而不是一组属性。因此,您需要允许params 中传递的每个收件人id 的属性。我认为这段代码应该这样做:

private
def recipient_params
  params.require(:recipient).permit(permit_for_each_recipient)
end

def permit_for_each_recipient
  params[:recipient].keys.inject({}){|h,k| h[k] = attributes_to_permit; h}
end

def attributes_to_permit
  [:contact_id, :group_id, :email_id, :var1, :var2, :var3]
end

【讨论】:

  • 这会停止错误,但由于某种原因它不会更新数据库中的数据
【解决方案2】:

评论后编辑答案

首先,一些好的docs可以帮助你更好地理解如何使用强参数。

现在,让我们尝试重构一下您的代码。请记住,大于 4 行的方法隐藏错误的风险更大,如果可以,请避免它们!

这是你的代码,除了将一些代码块移到子方法中之外,我什么也没做

def update_multiple
  #this cannot  work because there is no instance to update
  if Recipient.update(params[:recipient].keys, params[:recipient].values)
    send_unsuscribe_emails
    flash[:notice] = "recipients were updated"
    redirect_to root_path
  else
    render 'edit_multiple'
  end
end

private

def recipient_params
  params.require(:recipient).permit(:contact_id, :group_id, :email_id, :var1, :var2, :var3)
end

def send_unsuscribe_emails
  @email = Email.find_by_id(params[:email_id])
  #this cannot work because params[:recipient].keys does not return a list of ids (probably you want womethink like recipients_params[:contact_id])
  @listofcontacts = Recipient.where("id in (?)", params[:recipient].keys)
  @account = Account.find_by_id(@email.account_id)
  @listofcontacts.each do |f|
    send_unsuscribe_email(f)
  end
end

def send_unsuscribe_email(f)
  recipient_message = @email.message.gsub("VAR1", f.var1)
  contact = Contact.find_by_id(f.contact_id)
  @unsubscribe = Rails.application.message_verifier(:unsubscribe).generate(contact.id)
  UserEmails.send_email(@email, @account, contact.email, @unsubscribe, recipient_message).deliver_now
end

现在解决方案可能是这样的

def update_multiple
  @listofcontacts = Recipient.where("id in (?)", recipients_params[:contact_id])
  if @listofcontacts.update(recipient_params)
    send_unsuscribe_emails
    flash[:notice] = "recipients were updated"
    redirect_to root_path
  else
    render 'edit_multiple'
  end
end

private

def recipient_params
  params.require(:recipient).permit(:contact_id, :group_id, :email_id, :var1, :var2, :var3)
end

def send_unsuscribe_emails
  @email = Email.find_by_id(params[:email_id])
  @account = Account.find_by_id(@email.account_id)
  @listofcontacts.each do |f|
    send_unsuscribe_email(f)
  end
end

def send_unsuscribe_email(f)
  recipient_message = @email.message.gsub("VAR1", f.var1)
  contact = Contact.find_by_id(f.contact_id)
  @unsubscribe = Rails.application.message_verifier(:unsubscribe).generate(contact.id)
  UserEmails.send_email(@email, @account, contact.email, @unsubscribe, recipient_message).deliver_now
end

当然我不能这样测试它,它可能会在某个地方崩溃,但或多或​​少是这个想法。

【讨论】:

  • 这现在给了我错误undefined method 'keys' for nil:NilClass
  • 不错!这是由于参数如何被强参数“转换”。当您像这样声明它们 params.require(:recipient).permit(:contact_id, :group_id, :email_id, :var1, :var2, :var3) 您将收到类似 recipient: {contact_id: 1, group_id: 1, email_id: anemail@example.com, etc....} 的内容,这意味着您可以根据需要做两件事:Recipient.update(contact_id: recipient_params[:contact_id]) 仅更新一个属性或 Recipient.update(recipient_params) 更新所有内容
  • 道歉,但我没有关注,你介意提供更多关于我可以做的两件事的细节,因为记住这种方法是一次更新多条记录,我不明白如何这两件事有效。
  • 别担心!我们在这里提供帮助。试试这个。 if Recipient.update(recipient_params) 会发生什么?
  • 谢谢,这太令人沮丧了。这给了我错误wrong number of arguments (1 for 2)
猜你喜欢
  • 2014-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-28
  • 2013-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多