Ruby 在 Rails 上:ActiveModel::ForbiddenAttributesError 具有强参数
Ruby on Rails: ActiveModel::ForbiddenAttributesError with strong params
在我的 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
声明但无法正常工作。
评论后编辑答案
首先,一些好的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
当然我不能测试它,可能它会在某个地方崩溃,但或多或少是这样的想法。
问题出在这一行:
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
在我的 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
声明但无法正常工作。
评论后编辑答案
首先,一些好的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
当然我不能测试它,可能它会在某个地方崩溃,但或多或少是这样的想法。
问题出在这一行:
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