问题描述
地址模型
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>
记录“参数”,我看到所有值,但地址未创建。我相信错误在于shared_address_params
。有什么想法吗?
解决方法
我认为您只是迷失了该参数白名单中的间接性和复杂性。
您基本上想要的是:
def customer_params
params.require(:customer)
.permit(
address_attributes: {
physical_address_attributes: [:street,:foo,:bar,:baz],postal_address: [:street,: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,:baz]
end
如您所见,这里应该几乎没有增加任何复杂性,并且如果需要使其更加灵活,则可以向address_attributes
方法添加参数-毕竟,建立白名单只是简单的数组和哈希操作。 / p>
如果要处理将某种共享属性映射到这两种地址类型的操作,您实际上应该在模型中执行此操作,而不是使用业务逻辑来膨胀控制器。例如,通过为“虚拟属性”创建setter和getter:
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