Editing a form using fields_for in Rails

Let’s say you’d like to successfully create or update a nested form using fields_for. Our ‘parent’ will be a Post model and our ‘child’ will be a Tag model. It’s not hard, but there’s a bit of a gotcha that I found most other resources out there were leaving out.

Post (our parent)

We’ll use a Post model as our parent in this situation. Nothing out of the ordinary here. A post can have many tags and we’ll allow them to be destroyed if the model itself is.

# app/models/post.rb

class Post < ActiveRecord::Base
  has_many :tags, dependent: :destroy

  # Enable the building of complex forms
  accepts_nested_attributes_for :tags, allow_destroy: true

It’s important to include accepts_nested_attributes_forto allow you to save attributes on the associated Tag model. You get no Rails magic if you forget this.

Tag (our child)

Again, nothing special here. Just showing that it belongs to our post model

# app/models/tag.rb

class Tag < ActiveRecord::Base
  belongs_to :post

The Controllers

Now we get to some of the important stuff. There are a few things to take note of in the new and edit actions.

# /app/controllers/posts_controller.rb
def new
  @post = Post.new
  # Ensure we have a blank tag to start with
  1.times { @post.tags.build }

def edit
  @post = Post.find(params[:id])
  1.times { @post.tags.build }


def post_params
  params.require(:post).permit(:title, :body, tags_attributes:[:id, :title])

So, a few things here.

The View

We’re going to follow the common pattern of sharing a _form.html.erb partial between the new and edit views.

<!-- app/views/admin/posts/_form.html.erb -->

<%= form_for [:admin, @post] do |f| %>

  <%= render '/shared/form_errors', object: f.object %>

    <%= f.text_field :title, class:'form-control', placeholder:'Title' %>

    <%= f.text_area :body, class:'form-control', placeholder:'Body', rows:10 %>

  <h3>Post Tags</h3>
  <%= f.fields_for :tags do |tag_form| %>
      <%= tag_form.text_field :title, class:'form-control' %>
  <% end %>

    <%= f.submit "Save", class:'btn' %>

<% end %>

Now it should be obvious why we had to build an empty Tag object. If we hadn’t, we would have an error with tag_form.text_field :title when on the new view because tag_form would be null.

The fact that we’re using 1.times { @post.tags.build } in the edit action means we’ll always have an extra blank field should we want to add an additional tag.


Creating nested forms in Rails isn’t overly complicated. I just got hung up on the necessary Strong Parameters to permit, namely the nested :id.

Written by Matt Haliski
Consumer of tacos