Rails 来自相关模型的强大参数中不允许的参数创建
Unpermitted parameters in Rails strong params from related Model on create
我正在使用 Rails 中的表单创建一个模型的实例,并在创建时创建到另一个模型的连接 table。在我的 Accounts
控制器中,在验证 Account
的新实例已创建后,我根据在另一个模型的表单中选择的复选框创建了一个新的连接 table。
这是我的代码:
型号:
class Account < ApplicationRecord
has_many :account_authorizations
has_many :authorizations, through: :account_authorizations
accepts_nested_attributes_for :authorizations
end
class Authorization < ApplicationRecord
has_many :account_authorizations
has_many :accounts, through: :account_authorizations
end
class AccountAuthorization < ApplicationRecord
belongs_to :account
belongs_to :authorization
end
我的 Accounts
控制器:
class AccountsController < ApplicationController
before_action :find_account, only: [:show, :update, :destroy]
def index
@accounts = Account.all
end
def show
end
def new
@authorizations = Authorization.all
end
def create
byebug
@account = Account.new(account_params)
if @account.save
authorization_ids = params.permit![:authorization][:ids]
authorization_ids.each do |auth_id|
AccountAuthorization.create(authorization_id: auth_id, account_id: params[:id]) if !auth_id.empty?
end
flash[:notice] = "Account created sucsessfully"
redirect_to account_path(@account)
else
flash[:error] = @item.errors.full_messages.to_sentence
render :new
end
end
def edit
end
def update
@account = Account.update(account_params)
if @account.valid?
flash[:notice] = "Account created sucsessfully"
redirect_to accounts_path(@account)
else
flash[:error] = @item.errors.full_messages.to_sentence
render :edit
end
end
def destroy
@account.delete
redirect_to accounts_path
end
private
def find_account
@account = Account.find(params[:id])
end
def account_params
params.permit(
:account_name,
:account_address_line1,
:account_address_line2,
:account_city,
:account_state,
:account_zipcode,
:account_ppu,
:account_notes,
:contact_first_name,
:contact_last_name,
:contact_phone,
:contact_email
)
end
end
我试过使用 .permit!在参数上,但这不允许来自 Authorization
的 ID 传递到 AccountAuthorization
的新连接实例中。
没有通过的属性是:
Unpermitted parameters: :authenticity_token, :authorization, :commit
我也试过将它们放在 strong_params 方法的许可散列中。
** 更新 **
我的观点页面form
:
<% states = [ 'AL', 'AK', 'AS', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DC', 'DE', 'DC', 'FL', 'GA', 'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MH', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'MP', 'OH', 'OK', 'OR', 'PA' 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT' 'VA', 'WA', 'WV', 'WI', 'WY'] %>
<% ppu = ['Employment', 'Insurance', 'Law Enforement'] %>
<div class="row justify-content-center">
<div class="col-lg-16">
<h3>Create New Account</h3>
</div>
</div>
<div class="row justify-content-center">
<div class="col-lg-16">
<%= form_with(url: accounts_path, model: @account, local: true) do |f| %>
<%= f.text_field :account_name, class: "form-control", placeholder: "Account Name" %>
<%= f.text_field :account_address_line1, class: "form-control", placeholder: "address line 1" %>
<%= f.text_field :account_address_line2, class: "form-control", placeholder: "address line 2" %>
<%= f.text_field :account_city, class: "form-control", placeholder: "account city" %>
<%= f.select :account_state, options_for_select(states), { :prompt => true }, class: "form-control", include_blank: true, placeholder: "state" %>
<%= f.text_field :account_zipcode, class: "form-control", placeholder: "account zipcode" %>
<%= f.text_field :contact_first_name, class: "form-control", placeholder: "contact first name" %>
<%= f.text_field :contact_last_name, class: "form-control", placeholder: "contact last name" %>
<%= f.text_field :contact_phone, class: "form-control", placeholder: "conact phone" %>
<%= f.text_field :contact_email, class: "form-control", placeholder: "contact email" %>
<%= f.select :account_ppu, options_for_select(ppu), { :prompt => true }, class: "form-control", include_blank: true, placeholder: "ppu" %>
<%= f.text_area :account_notes, class: "form-control", placeholder: "Notes..." %>
<div class="d-flex justify-content-between flex-wrap">
<%= f.fields_for :authorization do |auth| %>
<div class="order-3 p-2 bd-highlight">
<%= auth.collection_check_boxes :ids, Authorization.all, :id, :auth_name %>
</div>
<% end %>
</div>
<%= f.submit "Create", class: 'btn btn-success' %>
<% end %>
</div>
</div>
我通常做的是这个(require(:account) 很重要):
def account_params
params.require(:account).permit(
:account_name,
# Rest of the params you want to permit...
authorization_ids: []
)
end
这将允许您在控制器操作中进行定期保存。
def create
@account = Account.new(account_params)
if @account.save
flash[:notice] = "Account created successfully"
redirect_to account_path(@account)
else
flash[:error] = @item.errors.full_messages.to_sentence
render :new
end
end
并且在表单中,如果您将它们显示为复选框。
<% Authorization.all.each do |authorization| %>
<%= check_box_tag 'account[authorization_ids][]', authorization.id, @account.authorizations.include?(authorization), id: dom_id(authorization), type: "checkbox" %>
<% end %>
这将遍历所有授权记录并将其显示在屏幕上,如果您 select 它们将存储在 authorization_ids 参数中,并且 rails 足够聪明接受这些参数并在 table.
中创建连接关联
顺便说一句,这是使用简单的形式,因此应稍微修改代码以获得所需的输入
您不需要手动创建加入 table 记录。
对于每个 has_many
和 has_many_and_belongs_to_many
关联 Rails 创建一个 _ids
setter 接受一个 id 数组。 Rails 将自动 create/delete 从输入中加入 table 行。
这与 collection helpers 完美匹配。
app/views/accounts/_form.html.erb
<%= form_with(model: account, local: true) do |f| %>
<div class="field">
<%= f.label :account_name %>
<%= f.text_field :account_name %>
</div>
# ... more inputs
<div class="field">
<%= f.label :authorization_ids, 'Authorizations' %>
<%= f.collection_checkboxes :authorization_ids,
Authorization.all, # the collection
:id, # value method
:name # label method
%>
</div>
<div class="actions">
<%= f.submit %>
<div>
<% end %>
app/views/accounts/new.html.erb
<%= render partial: 'form', account: @account %>
accounts/edit.html.erb
<%= render partial: 'form', account: @account %>
app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
before_action :find_account, only: [:show, :edit, :update, :destroy]
# ...
def create
@account = Account.new(account_params)
if @account.save
flash[:notice] = "Account created sucsessfully"
redirect_to @account
else
# render :new does not redirect so you need to use flash.now
# to display the flash message in this request
flash.now[:error] = @item.errors.full_messages.to_sentence
render :new
end
end
def edit
end
# This is how you update a resource.
def update
# don't check .valid? - it just tells you if the validations passed
# not if the actual DB update query was a successes
if @account.update(account_params)
flash[:notice] = "Account updated successfully"
redirect_to @account
else
flash.now[:error] = @item.errors.full_messages.to_sentence
render :edit
end
end
# ...
private
def account_params
params.require(:account).permit(
:account_name,
:account_address_line1,
:account_address_line2,
:account_city,
:account_state,
:account_zipcode,
:account_ppu,
:account_notes,
:contact_first_name,
:contact_last_name,
:contact_phone,
:contact_email,
account_ids: []
)
end
end
因为这从参数散列中切出了键 :account
,所以您不会得到未经许可的参数错误。 authenticity_token
是 Rails CSRF 保护令牌,commit
是单击提交表单的提交按钮的值。这些不应列入白名单或发送给您的模型
您通过将关键字参数 (account_ids: []
) 传递给 .permit
并将空数组作为值来将授权 ID 数组列入白名单。由于这是关键字参数,因此它必须位于位置参数列表之后。
您也不需要 accepts_nested_attributes_for
,除非您正在做一些比选择现有记录更高级的事情。但是在您掌握了 CRUD 的基础知识之前,您还没有完全准备好 accepts_nested_attributes_for
。
我正在使用 Rails 中的表单创建一个模型的实例,并在创建时创建到另一个模型的连接 table。在我的 Accounts
控制器中,在验证 Account
的新实例已创建后,我根据在另一个模型的表单中选择的复选框创建了一个新的连接 table。
这是我的代码:
型号:
class Account < ApplicationRecord
has_many :account_authorizations
has_many :authorizations, through: :account_authorizations
accepts_nested_attributes_for :authorizations
end
class Authorization < ApplicationRecord
has_many :account_authorizations
has_many :accounts, through: :account_authorizations
end
class AccountAuthorization < ApplicationRecord
belongs_to :account
belongs_to :authorization
end
我的 Accounts
控制器:
class AccountsController < ApplicationController
before_action :find_account, only: [:show, :update, :destroy]
def index
@accounts = Account.all
end
def show
end
def new
@authorizations = Authorization.all
end
def create
byebug
@account = Account.new(account_params)
if @account.save
authorization_ids = params.permit![:authorization][:ids]
authorization_ids.each do |auth_id|
AccountAuthorization.create(authorization_id: auth_id, account_id: params[:id]) if !auth_id.empty?
end
flash[:notice] = "Account created sucsessfully"
redirect_to account_path(@account)
else
flash[:error] = @item.errors.full_messages.to_sentence
render :new
end
end
def edit
end
def update
@account = Account.update(account_params)
if @account.valid?
flash[:notice] = "Account created sucsessfully"
redirect_to accounts_path(@account)
else
flash[:error] = @item.errors.full_messages.to_sentence
render :edit
end
end
def destroy
@account.delete
redirect_to accounts_path
end
private
def find_account
@account = Account.find(params[:id])
end
def account_params
params.permit(
:account_name,
:account_address_line1,
:account_address_line2,
:account_city,
:account_state,
:account_zipcode,
:account_ppu,
:account_notes,
:contact_first_name,
:contact_last_name,
:contact_phone,
:contact_email
)
end
end
我试过使用 .permit!在参数上,但这不允许来自 Authorization
的 ID 传递到 AccountAuthorization
的新连接实例中。
没有通过的属性是:
Unpermitted parameters: :authenticity_token, :authorization, :commit
我也试过将它们放在 strong_params 方法的许可散列中。
** 更新 **
我的观点页面form
:
<% states = [ 'AL', 'AK', 'AS', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DC', 'DE', 'DC', 'FL', 'GA', 'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MH', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'MP', 'OH', 'OK', 'OR', 'PA' 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT' 'VA', 'WA', 'WV', 'WI', 'WY'] %>
<% ppu = ['Employment', 'Insurance', 'Law Enforement'] %>
<div class="row justify-content-center">
<div class="col-lg-16">
<h3>Create New Account</h3>
</div>
</div>
<div class="row justify-content-center">
<div class="col-lg-16">
<%= form_with(url: accounts_path, model: @account, local: true) do |f| %>
<%= f.text_field :account_name, class: "form-control", placeholder: "Account Name" %>
<%= f.text_field :account_address_line1, class: "form-control", placeholder: "address line 1" %>
<%= f.text_field :account_address_line2, class: "form-control", placeholder: "address line 2" %>
<%= f.text_field :account_city, class: "form-control", placeholder: "account city" %>
<%= f.select :account_state, options_for_select(states), { :prompt => true }, class: "form-control", include_blank: true, placeholder: "state" %>
<%= f.text_field :account_zipcode, class: "form-control", placeholder: "account zipcode" %>
<%= f.text_field :contact_first_name, class: "form-control", placeholder: "contact first name" %>
<%= f.text_field :contact_last_name, class: "form-control", placeholder: "contact last name" %>
<%= f.text_field :contact_phone, class: "form-control", placeholder: "conact phone" %>
<%= f.text_field :contact_email, class: "form-control", placeholder: "contact email" %>
<%= f.select :account_ppu, options_for_select(ppu), { :prompt => true }, class: "form-control", include_blank: true, placeholder: "ppu" %>
<%= f.text_area :account_notes, class: "form-control", placeholder: "Notes..." %>
<div class="d-flex justify-content-between flex-wrap">
<%= f.fields_for :authorization do |auth| %>
<div class="order-3 p-2 bd-highlight">
<%= auth.collection_check_boxes :ids, Authorization.all, :id, :auth_name %>
</div>
<% end %>
</div>
<%= f.submit "Create", class: 'btn btn-success' %>
<% end %>
</div>
</div>
我通常做的是这个(require(:account) 很重要):
def account_params
params.require(:account).permit(
:account_name,
# Rest of the params you want to permit...
authorization_ids: []
)
end
这将允许您在控制器操作中进行定期保存。
def create
@account = Account.new(account_params)
if @account.save
flash[:notice] = "Account created successfully"
redirect_to account_path(@account)
else
flash[:error] = @item.errors.full_messages.to_sentence
render :new
end
end
并且在表单中,如果您将它们显示为复选框。
<% Authorization.all.each do |authorization| %>
<%= check_box_tag 'account[authorization_ids][]', authorization.id, @account.authorizations.include?(authorization), id: dom_id(authorization), type: "checkbox" %>
<% end %>
这将遍历所有授权记录并将其显示在屏幕上,如果您 select 它们将存储在 authorization_ids 参数中,并且 rails 足够聪明接受这些参数并在 table.
中创建连接关联顺便说一句,这是使用简单的形式,因此应稍微修改代码以获得所需的输入
您不需要手动创建加入 table 记录。
对于每个 has_many
和 has_many_and_belongs_to_many
关联 Rails 创建一个 _ids
setter 接受一个 id 数组。 Rails 将自动 create/delete 从输入中加入 table 行。
这与 collection helpers 完美匹配。
app/views/accounts/_form.html.erb
<%= form_with(model: account, local: true) do |f| %>
<div class="field">
<%= f.label :account_name %>
<%= f.text_field :account_name %>
</div>
# ... more inputs
<div class="field">
<%= f.label :authorization_ids, 'Authorizations' %>
<%= f.collection_checkboxes :authorization_ids,
Authorization.all, # the collection
:id, # value method
:name # label method
%>
</div>
<div class="actions">
<%= f.submit %>
<div>
<% end %>
app/views/accounts/new.html.erb
<%= render partial: 'form', account: @account %>
accounts/edit.html.erb
<%= render partial: 'form', account: @account %>
app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
before_action :find_account, only: [:show, :edit, :update, :destroy]
# ...
def create
@account = Account.new(account_params)
if @account.save
flash[:notice] = "Account created sucsessfully"
redirect_to @account
else
# render :new does not redirect so you need to use flash.now
# to display the flash message in this request
flash.now[:error] = @item.errors.full_messages.to_sentence
render :new
end
end
def edit
end
# This is how you update a resource.
def update
# don't check .valid? - it just tells you if the validations passed
# not if the actual DB update query was a successes
if @account.update(account_params)
flash[:notice] = "Account updated successfully"
redirect_to @account
else
flash.now[:error] = @item.errors.full_messages.to_sentence
render :edit
end
end
# ...
private
def account_params
params.require(:account).permit(
:account_name,
:account_address_line1,
:account_address_line2,
:account_city,
:account_state,
:account_zipcode,
:account_ppu,
:account_notes,
:contact_first_name,
:contact_last_name,
:contact_phone,
:contact_email,
account_ids: []
)
end
end
因为这从参数散列中切出了键 :account
,所以您不会得到未经许可的参数错误。 authenticity_token
是 Rails CSRF 保护令牌,commit
是单击提交表单的提交按钮的值。这些不应列入白名单或发送给您的模型
您通过将关键字参数 (account_ids: []
) 传递给 .permit
并将空数组作为值来将授权 ID 数组列入白名单。由于这是关键字参数,因此它必须位于位置参数列表之后。
您也不需要 accepts_nested_attributes_for
,除非您正在做一些比选择现有记录更高级的事情。但是在您掌握了 CRUD 的基础知识之前,您还没有完全准备好 accepts_nested_attributes_for
。