嵌套关联不保存

Nested association not saving

我有一个客户的嵌套关联。设置是这样的:

地址模型

  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 方法添加参数 - 毕竟构建白名单只是简单的数组和哈希操作.

如果你想处理将某种共享属性映射到两种地址类型,你真的应该在模型中进行,而不是用业务逻辑使控制器膨胀。例如,通过为“虚拟属性”创建 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