Nested Attributes Not Setting Child IDs

Developing in Rails 5 recently I was struggling to think of why my child record IDs were not being set automatically on create. I thought this happened automatically. Then I discovered that setting inverse_of in the model is vital for accessing parent models from the child without relying on a pre-existing physical record (e.g. when you are creating new records). It turns out it’s been like this since Rails 2. How blind was I!

Here’s how it works:

class Publisher < ApplicationRecord
  has_many :articles, index_errors: true, dependent: :destroy, inverse_of: :publisher
  accepts_nested_attributes_for :articles, allow_destroy: true

class Article < ApplicationRecord
  belongs_to :publisher, inverse_of: :articles
  has_many :comments, index_errors: true, dependent: :destroy, inverse_of: :article
  accepts_nested_attributes_for :comments, allow_destroy: true, reject_if: proc { |item| item[:message].blank? }

class Comment < ApplicationRecord
  belongs_to :article, inverse_of: :comments

So using inverse_of allows me to access self.article from the Comment model even when the physical record has yet to be created.

2 thoughts on “Nested Attributes Not Setting Child IDs

  1. Hello! I just stumbled across this exact problem (Working on Rails 6 now!) and then i stumbled across your blog. Very informative!

    I’ve read somewhere that this only happens when the Model which we are creating through ‘nested_attributes’ is alphabetically lower than the model with the ‘has_many’ relation. Do you have any idea if this is true?

    What i mean is, If you have a model ‘Prison’ and another ‘Criminal’, if ‘Prison’ has many ‘Criminals’ and you are creating both at the same time through nested attributes, it will fail only because of alphabetical order. But if you had ‘Law’ and ‘Prison’, and a ‘Law’ had many ‘Prisons’ and you are creating both at the same time through nested attributes, creation will not fail, because ActiveRecord creates ‘Law’ first, and then ‘Prisons’.

    What this means is that, without ‘inverse_of’, ActiveRecord fetches from the Database, and our data has not yet been persisted, while with ‘inverse_of’, it fetches from memory, so it fixes the problem.

    Anyways, thanks for the very useful info!

  2. Thanks for your comment Guido. I’m not sure of the answer to your question at this time but there’s certainly logic to what you suggest. I’ll have to do some more digging on this when I have time!

Leave a Reply

Your email address will not be published.