{"id":1645,"date":"2013-06-26T10:59:39","date_gmt":"2013-06-26T14:59:39","guid":{"rendered":"http:\/\/blogs.law.harvard.edu\/lianaleahy\/?p=1645"},"modified":"2013-06-26T10:59:39","modified_gmt":"2013-06-26T14:59:39","slug":"autosaveassociation","status":"publish","type":"post","link":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/2013\/06\/26\/autosaveassociation\/","title":{"rendered":"AutosaveAssociation"},"content":{"rendered":"<p>Recently, I wanted to utilize the <a href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveRecord\/AutosaveAssociation.html\">AutosaveAssociation<\/a> module which (wrapped in a transaction) autosaves a model record when the parent is saved.  I&#8217;ll use the example from the api docs:<\/p>\n<pre>\r\npost = Post.new(title: 'ruby rocks')\r\npost.comments.build(body: 'hello world')\r\npost.save # =&gt; saves both post and comment\r\n<\/pre>\n<p>But because I had a validation on comments that requires presence of post, it was attempting to validate comments before post was created.  So instead of saving all my models in one swoop, it threw a validation error.<\/p>\n<p>Wut?  This should just work!  I fussed with it for hours and the only workaround appeared to be to call <em>save<\/em> with <em>validation: false<\/em>.<\/p>\n<p>Ew. Anyone else feel queasy?<\/p>\n<p>Turns out the missing piece was to add <strong>inverse_of<\/strong> on my model associations.  Here&#8217;s an awesome explanation from my coworker <a href=\"https:\/\/twitter.com\/mdaubs83\">@mdaubs83<\/a>:<\/p>\n<blockquote><p>The :inverse_of option is needed here to inform AR about the inverse association so that the Comment instance returned from build() can reference the original Post instance. This in turn allows the Comment instance to see the id of the Post instance after it&#8217;s saved and update it&#8217;s foreign key accordingly when AutosaveAssociation calls save() on associated objects. There are other benefits to using :inverse_of, we should probably consider adding the option to all associations. Here&#8217;s evidence of the issue and how adding inverse_of solves it:<\/p>\n<pre>\r\npost.comments.build(user_id: user.id)\r\n\r\n# has_many :comments\r\npost.comments.first.post.object_id == post.object_id\r\n # =&gt; false\r\n\r\n# has_many :comments, :inverse_of =&gt; :post\r\npost.comments.first.post.object_id == post.object_id\r\n # =&gt; true\r\n<\/pre>\n<p>See also &#8220;Bi-directional associations&#8221; in <a href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveRecord\/Associations\/ClassMethods.html\">Rails API Docs<\/a><\/p><\/blockquote>\n<p>Thanks, Matt!  Sadly, <strong>inverse_of<\/strong> is never mentioned in the <a href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveRecord\/AutosaveAssociation.html\">AutosaveAssociation<\/a> docs.  But I noticed this line in the <a href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveRecord\/Associations\/ClassMethods.html\">ActiveRecord Association<\/a> docs.<\/p>\n<blockquote><p>If you are using a belongs_to on the join model, it is a good idea to set the :inverse_of option on the belongs_to, which will mean that the following example works correctly (where tags is a has_many :through association):<\/p>\n<pre>\r\n@post = Post.first\r\n@tag = @post.tags.build :name =&gt; \"ruby\"\r\n@tag.save\r\n<\/pre>\n<p>The last line ought to save the through record (a Taggable). This will only work if the :inverse_of is set:<\/p>\n<pre>\r\nclass Taggable ActiveRecord::Base\r\n  belongs_to :post\r\n  belongs_to :tag, :inverse_of =&gt; :taggings\r\nend\r\n<\/pre>\n<\/blockquote>\n<p>Err.  Okay.  Would have been useful to see that little tidbit included in the <a href=\"http:\/\/api.rubyonrails.org\/classes\/ActiveRecord\/AutosaveAssociation.html\">AutosaveAssociation<\/a> docs as well!<\/p>\n<p>But good news!  Matt also tells me that Rails 4.1 is poised to support automatic <strong>inverse_of<\/strong> detection.  So once we all upgrade we get <strong>inverse_of<\/strong> goodness for free.  W00t!<\/p>\n<pre>\r\nCommit d6b03a3 to rails\/rails by wangjohn\r\n<a href=\"https:\/\/github.com\/rails\/rails\/commit\/d6b03a376787ec9c9e934e5688a38c576f2e39b7\">https:\/\/github.com\/rails\/rails\/commit\/d6b03a376787ec9c9e934e5688a38c576f2e39b7<\/a>\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Recently, I wanted to utilize the AutosaveAssociation module which (wrapped in a transaction) autosaves a model record when the parent is saved. I&#8217;ll use the example from the api docs: post = Post.new(title: &#8216;ruby rocks&#8217;) post.comments.build(body: &#8216;hello world&#8217;) post.save # =&gt; saves both post and comment But because I had a validation on comments that [&hellip;]<\/p>\n","protected":false},"author":1911,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2850,2403],"tags":[],"class_list":["post-1645","post","type-post","status-publish","format-standard","hentry","category-professional","category-ruby-on-rails"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/posts\/1645","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/users\/1911"}],"replies":[{"embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/comments?post=1645"}],"version-history":[{"count":43,"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/posts\/1645\/revisions"}],"predecessor-version":[{"id":1688,"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/posts\/1645\/revisions\/1688"}],"wp:attachment":[{"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/media?parent=1645"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/categories?post=1645"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/lianaleahy\/wp-json\/wp\/v2\/tags?post=1645"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}