Rails – Maintaining Active Record Associations through edit form partials the hard, then easy, way

Today’s post is something of a cautionary tale. It’s from a friend of mine who would totally work on a solution for days and then find a much easier and efficient solution that renders all of his work useless right before he writes a post about said now ridiculous solution. Obviously it’s not about me – by reading further you are agreeing to believe that. By reading this line you’ve accepted those terms.

So the story begins with my friend who had created a custom CRM system for his current employer (just a crazy coincidence, obviously). When viewing a company, users can also see/create/edit/delete all of the contact and notes that are associated with each company. The Company model has_many :notes and has_many :contacts. Note’s and Contact’s models, subsequently, belongs_to :company.

The views are set up so that when we access the Company#show view we will also see buttons to create new notes and contacts as well as see each note and contact associated with that :company_id. Everything was working well until a few users told me (I mean him, and you’re legally bound to believe that) that when they edited the information for a contact and then saved it, the contact disappeared from the company show view.

In order to log the company_id when a user was creating a new contact I originally supplied a hidden field in the _form.html.erb partial:

app/views/contacts/_form.html.erb
<%= form_for(@contact) do |f| %>
# several lines lower...
  <div class="field">
    <%= f.hidden_field :company_id, :value => params[:company_id] %>
  </div>

Meanwhile on the Company#show view the button to create a new contact was coded so as to send the :company_id through to this form:

app/views/companies/show.html.erb
  <li class="btn btn-success btn-md"><%= link_to("Add Contact", new_contact_path(:company_id => @company.id)) %></li>

This worked perfectly, however when all of the contacts were listed (in a partial rendered on app/views/companies/show.html.erb the edit button was coded as such:

app/views/companies/_companycontacts.html.erb
<% @contacts.each do |contact| %>
# several lines lower... 
  <%= link_to("Edit", edit_contact_path(contact), class: "btn btn-xs btn-primary") %>

Which left the hidden field on the form partial blank. So now when the contact was updated, the :company_id would be updated to nil and therefore no longer associated with the parent company.

So the simple solution seemed to be to insert the @company param in the edit button code. Something as simple as this:

app/views/companies/show.html.erb
<%= link_to("Edit", edit_contact_path(contact, params: {company_id: @company.id}), class: "btn btn-xs btn-primary") %>

This will send the @company.id to our edit form and viola! – problem solved, right? No, not exactly. Turns out this opens up the system to XSS attacks – I problem I read about in this article. So, since this was the one line of code that my friend had identified that could solve his problem, this was obviously the only line of code that could be altered to solve the new problem as well.

What followed was three days of tunnel vision, frantic googling, trial and error (so many errors), and finally a solution that on it’s own could be published as a novella. I won’t even post the code here – it is so overthought and convoluted it could cause an aneurysm. It was also – my friend thought – going to make him an internet hero once he published it in this blog post. I mean his blog post. Whatever.

Suddenly, when logging into his blog he had a thought. He’d try something else completely just for giggles. It wouldn’t work – it couldn’t – it was too simple to work. It did work. What followed can only be described as “all of the impotent rage”. Here’s what actually works. It takes days of work, and reams of code and boils it down to about 4 words. First off – keep the code for the edit button the way it always was:

app/views/companies/show.html.erb
<%= link_to("Edit", edit_contact_path(contact), class: "btn btn-xs btn-primary") %>

And instead, only alter the partial for the contact form that is going to load when you click on edit contact. Take that one line for the hidden field that relays company_id from the bottom of the form and place it at the top inside of a conditional. A simple conditional that will check to see if this field even needs to exist. Because it obviously only needs to exist if this is the first time a contact is being created. After that we never need to mess with it again. Obviously. So frickin obviously.

app/views/contacts/_form.html.erb
<% if @contact.new_recod? %>
  <div class="field>
    <%= f.hidden_field :comapny_id, :value => params[:company_id] %>
  </div>
<% end %>

And then the rest of your form follows below this conditional – set to show up no matter what…. This will work because the link for Add new Contact will pass along the :company_id

app/views/companies/show.html.erb
<%= link_to("Add Contact", new_contact_path(:company_id => @company.id)) %>

I suppose there’s a lesson here. Something perhaps about taking a step back and re-evaluating the problem and potential solutions more often. Something perhaps, about the futility life and how trying to solve these problems is a proxy war for our constant and unwinnable struggle against our inevitable death. Or maybe it’s just the first one – who knows?

Either way – don’t forget our contract from the beginning. This happened to my friend, I am infallible. I’d rather stay friends and not get litigious.

Loading Facebook Comments ...

Leave a Reply

Your email address will not be published. Required fields are marked *