嵌套资源和强参数应该使用'model'还是'model_attributes'?为什么? (未经许可的参数:型号)

问题描述 投票:0回答:2

我有一个嵌套的形式,每个成功的@address创建一个@purchase

我的模式和模型关联已设置,以便Purchase保存其他所有内容的外键:

address.rb

belongs_to :user
has_many :purchases, foreign_key: :address_id
has_many :items, foreign_key: :address_id

purchase.rb

belongs_to :sender, class_name: "User"
belongs_to :recipient, class_name: "User"
belongs_to :address
belongs_to :item
accepts_nested_attributes_for :address

item.rb的

belongs_to :user
has_many :purchases, foreign_key: :item_id
belongs_to :address

我发现some answers建议将控制器中的嵌套属性参数列入白名单,控制器中使用.permit(address_attributes: [:name])而不是.permit(address: [:name]),但两者都没有创建记录。

purchases_controller.rb

def confirmation
 @item = Item.friendly.find(params[:item_id])
 @address = @transaction_wizard.transaction.build_address
end

def confirm
 current_step = params[:current_step]
 @item = Item.friendly.find(params[:item_id])
 @transaction_wizard = wizard_transaction_for_step(current_step)
 @transaction_wizard.transaction.attributes = address_params
 session[:transaction_attributes] = @transaction_wizard.transaction.attributes

 if @transaction_wizard.valid?
  next_step = wizard_transaction_next_step(current_step)
  create and return unless next_step

  redirect_to action: next_step
 else
  redirect_to action: current_step
 end
end

def create
 if @transaction_wizard.transaction.save
  redirect_to root_path, notice: "Bid sent. If you would like to cancel before it's approved, you can do so #{view_context.link_to('here', transactions_sent_unapproved_path)}.".html_safe
 else
  redirect_to item_path(@transaction_wizard.transaction.item), notice: 'There was a problem making this bid.'
 end
end

private
def address_params
 params.require(:transaction_wizard).permit(address_attributes: [:name, :street, :street_2, :city, :state, :zip_code])
end

无论我使用address_attributes还是address,结果都是一样的:

Parameters: {"transaction_wizard"=>
             {"address"=>
              {"name"=>"name", 
               "street"=>"123 st", 
               "street_2"=>"apt 5", 
               "state"=>"AL", 
               "zip_code"=>"48489"}
             }, 
             "current_step"=>"confirmation", 
             "commit"=>"Complete offer", 
             "item_id"=>"friendly-item-id"
            }
Unpermitted parameter: address

这是我的表格:

<%= form_for @transaction_wizard, as: :transaction_wizard, url: confirm_item_transaction_wizard_path(@item) do |f| %>
 <%= f.fields_for :address do |address| %>
  <%= address.text_field :name %>
  <%= address.text_field :street %>
  <%= address.text_field :street_2 %>
  <%= address.text_field :city %>
  <%= address.text_field :state %>
  <%= address.text_field :zip_code %>
 <% end %>

 <%= f.submit "Submit" } %>
<% end %>

是什么赋予了?如何让我的控制器接受这些参数?


更新:将address更改为addresses要求我将控制器中的@address变量更改为@address = @transaction_wizard.transaction.build_addresses。这会导致uninitialized constant Transaction::Addresses错误。

ruby-on-rails ruby nested-forms
2个回答
1
投票

你应该使用address_attributes。问题是你的关系:

要使用accepts_nested_attributes_for,你必须使用has_manyhas_one,并使用belongs_to

在你的情况下,关系是倒置的,请参阅:

购买belongs_to地址

地址has_many购买

您无法创建地址,因为根据您的关系,地址是购买的父模型,请参阅this link

嵌套属性是一种允许您通过其关联父级保存记录属性的功能。


1
投票

您可以通过belongs_to关联设置嵌套属性,但必须将关联设置为可选(除非您使用的是Rails 4或更早版本):

class Address < ApplicationRecord
  has_many :purcases
end

class Purchase < ApplicationRecord
  belongs_to :address, optional: true
  accepts_nested_attributes_for :address
end

require 'rails_helper'

RSpec.describe Purchase, type: :model do
  it "accepts nested attributes" do
    purchase = Purchase.create!(address_attributes: { name:  'foo', street: 'bar' })
    address = purchase.address
    expect(address.persisted?).to be_truthy
    expect(address.name).to eq 'foo'
    expect(address.street).to eq 'bar'
  end
end

这是因为嵌套属性是为了与has_one关联一起构建的,因此在嵌套记录之前验证“父”记录,以便可以在嵌套记录上设置外键(父ID)。

你应该将address_attributes列入白名单。嵌套属性根据第一个参数创建一个setter:

accepts_nested_attributes_for :foo
accepts_nested_attributes_for :bars

这将接受foo_attributes=bars_attributes=。这当然应该遵循你的关联的多样化(单数表示has_one / belongs_to,复数表示has_many)。

模型上的address= setter与嵌套属性无关 - 它由belongs_to :address关联创建并带有一个Address实例。事实上,如果你将那个参数列入白名单,你将得到一个ActiveRecord::AssociationTypeMismatch例外。

irb(main):004:0> Purchase.new.address = { street: 'foo' }
ActiveRecord::AssociationTypeMismatch: Address(#70264542836680) expected, got {:street=>"foo"} which is an instance of Hash(#70264511691080)
    from (irb):4

您还需要将表单设置为:

<%= f.fields_for :address do |address| %>
  # ...
<% end %>

使用fields_for时,只需传递关联名称即可。您不需要传递实例并使用as: :address给出错误的参数。

© www.soinside.com 2019 - 2024. All rights reserved.