Nested file uploads in Rails
This post is about how I solved a form submission with multiple files from a nested fields_for
by explicitly adding multipart: true
on the parent form_with
.
I have been building a secondhand marketplace for Bangladesh. Although I am a front-end developer, I needed a full-stack framework that would let me deploy as quickly as possible. I picked Ruby on Rails for building. It seemed like the fastest way to release something.
Recently, I was working on a nested form for a one-to-one relationship:
class Product < ApplicationRecord
has_one :book
has_many_attached :images
end
class Book < ApplicationRecord
belongs_to :product
end
I wanted to render a form for multiple images for the Post
model. According to the guide, this is how you do it:
<%= form_with model: book do |form| %>
<%= form.label :pages %>
<%= form.number_field :pages %>
<div>
<%= fields_for :product, book.product do |product_form| %>
<%= product_form.label :images %>
<%= product_form.file_field :images, multiple: true, accept: "image/*" %>
<%= product_form.label :name %>
<%= product_form.text_field :name %>
<% end %>
</div>
<%= form.submit %>
<% end %>
It wouldn't work.
After a while, I wondered if the encoding attribute, enctype
, was set correctly. For uploading files through a form, the enctype
must be set to multipart/form-data
.
According to the Rails API documentation, using file_field within a form_with
automatically sets the encoding of the form to multipart/form-data
.
I inspected the source for the form. There was no enctype
set on the generated form!
The fix for this issue was adding multipart: true
to the form.
+<%= form_with model: book, multipart: true do |form| %>
-<%= form_with model: book, do |form| %>
<%= form.label :pages %>
<%= form.number_field :pages %>
<div>
<%= fields_for :product, book.product do |product_form| %>
<%= product_form.label :images %>
<%= product_form.file_field :images, multiple: true, accept: "image/*" %>
<%= product_form.label :name %>
<%= product_form.text_field :name %>
<% end %>
</div>
<%= form.submit %>
<% end %>
The fields_for
around the file_field
must have prevented the default behaviour of setting the encoding for the form.
I might not have figured this out if I didn't know forms require the correct encoding type to be set. Goes to show how important it is to understand the foundational web technologies if you are building for the web.