我有一个嵌套的形式,每个成功的@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
错误。
你应该使用address_attributes
。问题是你的关系:
要使用accepts_nested_attributes_for
,你必须使用has_many
或has_one
,并使用belongs_to
。
在你的情况下,关系是倒置的,请参阅:
购买belongs_to地址
地址has_many购买
您无法创建地址,因为根据您的关系,地址是购买的父模型,请参阅this link。
嵌套属性是一种允许您通过其关联父级保存记录属性的功能。
您可以通过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
给出错误的参数。