【问题标题】:Unpermitted parameters in Rails strong params from related Model on create在创建时来自相关模型的 Rails 强参数中不允许的参数
【发布时间】:2020-03-30 19:35:18
【问题描述】:

我在 Rails 中使用表单来创建一个模型的实例,并在创建时创建一个连接表到另一个模型。在我的Accounts 控制器中,在验证Account 的新实例已创建后,我根据表单中从另一个模型中选择的复选框创建一个新的连接表。

这是我的代码:

型号:

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>

【问题讨论】:

  • 您能否发布表单,或者至少发布您用于选择授权 ID 的代码

标签: ruby-on-rails nested-attributes strong-parameters


【解决方案1】:

您无需手动创建连接表记录。

对于每个 has_manyhas_many_and_belongs_to_many 关联,Rails 都会创建一个 _ids 设置器,它接受一个 id 数组。 Rails 将自动从输入中创建/删除连接表行。

这与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

由于这会从 params 哈希中切出密钥 :account,因此您不会收到 unpermitted parameters 错误。 authenticity_token 是 Rails CSRF 保护令牌,commit 是点击提交表单的提交按钮的值。这些不应被列入白名单或发送给您的模型

您通过将关键字参数 (account_ids: []) 传递给 .permit 并以空数组作为值,将授权 ID 数组列入白名单。由于这是一个关键字参数,它必须在位置参数列表之后。

您也不需要accepts_nested_attributes_for,除非您正在做一些比选择现有记录更高级的操作。但是,在您掌握 CRUD 的基础知识之前,您还没有完全准备好使用 accepts_nested_attributes_for

【讨论】:

  • 这太接近了,我从你的回复中学到了很多。当我需要(:account) 时,我仍然会得到ActionController::ParameterMissing (param is missing or the value is empty: account)
  • 这很奇怪。你看过日志中传入的参数了吗?
  • 这真是出乎意料,表明您的应用程序中确实出现了问题。您确实需要通过查看呈现的表单来开始调试。输入的名称属性应类似于 'name="account[account_name]"' 。您还可以在浏览器中查看网络选项卡并查看原始请求正文。
  • 如果表单/请求看起来像预期的那样,那么您的应用程序中的某些东西正在转换参数。这只发生在这条路线还是您的所有路线?
  • 这里是传递给控制器​​的参数:codeshare.io/5vlM8l
【解决方案2】:

我通常做的是这个(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 %>

这将遍历所有授权记录并将其显示在屏幕上,如果您选择它们​​,它们将存储在 authentication_ids 参数中,并且 rails 足够聪明,可以接受这些参数并在该表中创建连接关联。

顺便说一句,这是使用简单的形式,因此应稍微修改代码以获得所需的输入

【讨论】:

  • 你已经走到一半了,然后你就趴在表格里了。只需使用集合助手而不是不可读的 300 字符行。
  • 是的,你是对的,我已经有一段时间没有使用 rails 视图了,更不用说使用简单的表单了。
  • 我遇到了params.require(:account) 的问题。我返回了一个错误,说它是“nil”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多