我有一个客户的嵌套关联。设置如下:
- 客户只有一个地址
- 地址具有物理地址
- 地址有postal_addrss
地址模型
belongs_to :customer, optional: true # Yes, optional
has_one :physical_address, dependent: :destroy
has_one :postal_address, dependent: :destroy
accepts_nested_attributes_for :physical_address
accepts_nested_attributes_for :postal_address
邮政地址模型
# same for physical_address.rb
belongs_to :address
客户控制器:
def create
@customer = current_user.customers.build(customer_params)
if @customer.save
return puts "customer saves"
end
puts @customer.errors.messages
#redirect_to new_customer_path
render :new
end
private
def customer_params
params.require(:customer).permit(
address_attributes: address_params
)
end
def address_params
return ([
postal_address_attributes: shared_address_params,
#physical_address_attributes: shared_address_params
])
end
def shared_address_params
params.fetch(:customer).fetch("address").fetch("postal_address").permit(
:street, etc...
)
end
客户型号:
has_one :address, dependent: :destroy
accepts_nested_attributes_for :address
创建了一个客户,但没有创建地址。这是表格,例如:
<form>
<input name="customer[address][postal_address][street]" value="Foo Street" />
</form>
日志记录";params";,我看到了所有的值,但地址并没有创建。我认为错误在于shared_address_params
。有什么想法吗?
我认为您只是在参数白名单中迷失了自己的间接性和复杂性。
你基本上想要的是:
def customer_params
params.require(:customer)
.permit(
address_attributes: {
physical_address_attributes: [:street, :foo, :bar, :baz],
postal_address: [:street, :foo, :bar, :baz]
}
)
end
正如您在这里看到的,您需要参数键customer[address_attributes]
,而不仅仅是customer[address]
。
现在让我们重构以减少重复:
def customer_params
params.require(:customer)
.permit(
address_attributes: {
physical_address_attributes: address_attributes,
postal_address: address_attributes
}
)
end
def address_attributes
[:street, :foo, :bar, :baz]
end
正如您所看到的,这里应该很少增加复杂性,如果您需要使其更灵活,请向address_attributes
方法添加参数——毕竟,构建白名单只是简单的数组和哈希操作。
如果你想处理将某种共享属性映射到这两种地址类型,你真的应该在模型中完成,而不是用业务逻辑夸大控制器。例如,通过为";虚拟属性":
class Address < ApplicationController
def shared_address_attributes
post_address_attributes.slice("street", "foo", "bar", "baz")
end
def shared_address_attributes=(**attrs)
# @todo map the attributes to the postal and
# visiting address
end
end
这样,你只需像设置其他属性一样设置表单和白名单,控制器就不需要关心细节。
def customer_params
params.require(:customer)
.permit(
address_attributes: {
shared_address_attributes: address_attributes,
physical_address_attributes: address_attributes,
postal_address: address_attributes
}
)
end