ruby-on-railsactiverecordnested-attributes

How to automatically assign parent ID as foreign key when using nested attributes?


An action creates records based on multiple layers of nested attributes, upon the submission of a form. However, the ID of the root record, which I call .save on, is not propagating downwards to the associations.

Here's a simplified example:

class A
  has_many :bs
  # Note that A has direct access to C, without going through B
  has_many :cs
  accepts_nested_attributes_for :b
end

class B
  # B has an :a_id field
  belongs_to :a
  has_many :cs
  accepts_nested_attributes_for :c
end

class C
  # note that C has a "circular relationship" to A, 
  # either directly through the :a_id field, or indirectly through B
  belongs_to :a
  belongs_to :b
end

Given the model definitions above, this is an example payload:

{
  a: {
    name: "Model A",
    bs_attributes: [{
      name: "Model B",
      cs_attributes: [{
        name: "Model C"
      }]
    }]
  }
}

And this payload will be fed into:

A.create(payload)

When that gets persisted on the database and A is assigned an ID, I expect this ID to be automatically set as a foreign key on both children (B) and grand-children (C) records. Is that something that can be accomplished using Rails built-in functions, with simple effort?

An example of solution I could think of would be adding a before_save callback on associations to manually set the :a_id field. However this feels like it could be accomplished in simpler ways.

Currently working on Rails 5 but any guidance is appreciated.


Solution

  • Using inverse_of to link the associations should work for the nested attributes. Example, A to B:

    class A
      has_many :bs, inverse_of: :a
      accepts_nested_attributes_for :b
    end
    
    class B
      belongs_to :a, inverse_of: :bs
    end
    

    Same thing for C to B.

    A to C is a bit special. Setting c.a = b.a in before_validation callback on C is an option.