There's an echo in my head

日々のメモ。

accepts_nested_attributes_forを多段で使う

複数の関連を跨いでaccepts_nested_attributes_forを使う場合の話。

たとえば

Organization has_many Groups
Group has_many Employees

というモデルに対して、1度にまとめてこれらのレコードを作成したい場合には次のようにする。

まずはクラスの定義:

class Organization
  has_many :groups, inverse_of: :organization

  accepts_nested_attributes_for :groups

  # attributes:
  #   name:string
end

class Group
  belongs_to :organization, inverse_of: :groups
  has_many :employees, inverse_of: :group

  accepts_nested_attributes_for :employees

  validates :organization, presence: true

  # attributes:
  #   name:string
end

class Employee
  belongs_to :group, inverse_of: :employees

  validates :group, presence: true

  # attributes:
  #   name:string
end

:inverse_ofオプションで親から自分を参照するときの関連名を指定する必要があるっぽい。これを指定していなかったら、バリデーション時にすでに作成されていたであろう親(EmployeeからGroup)を参照できなくてエラーが出た。

レコードを作成するには次のようなハッシュ構造を渡す:

Organization.create!({
  name: "組織名",
  groups_attributes: [
    {
      name: "グループ名",
      employees_attributes: [
        {
          name: "人名"
        }
      ]
    }
  ]
})

関連先の属性は#{関連名}_attributesというキーにハッシュの配列で渡す*1

これをstrong_parametersで使うには次のように指定する:

params.require(:organization).permit(
  :name,
  {
    groups_attributes: [
      :name,
      {
        employees_attributes: [
          :name
        ]
      }
    ]
  }
)

createだけならこれでいいけど、もしupdateも行う場合にはxxx_attributesに:idも含める。

フォームはfields_forで次のような感じ:

(あとで書く)

*1:has_oneの場合にはハッシュをそのまま渡す

このブログに出てくるコードスニペッツは、引用あるいは断りがない限りMITライセンスです。