Search code examples
ruby-on-railsformsfile-uploadrails-activestorage

ActiveStorage multiple upload with intermediate model


The goal

I want to upload multiple files. So one Intervention can have multiple Uploads and each Upload has one attached file to it. This way, each Upload can have one file attached with different status, name, visibility, etc. instead of having one Upload with has_many_attached


What I have done

I have one Intervention model that can have many uploads :

class Intervention < ApplicationRecord
  has_many :uploads, dependent: :destroy
  accepts_nested_attributes_for :uploads, :allow_destroy => true
end

Each uploads has one attached file using ActiveStorage :

class Upload < ApplicationRecord
  belongs_to :intervention
  has_one_attached :file
end

In my interventions_controller I do :

def new
  @intervention = Intervention.new
  @intervention.uploads.build
end

def create
  @intervention = Intervention.new(intervention_params)
  # + default scaffolded controller [...]
end

def intervention_params
  params.require(:intervention).permit(:user_id, :comment, uploads_attributes: [:status, :file])
end

In my form I have :

<%= form.fields_for :uploads, Upload.new do |uploads_attributes|%>
    <%= uploads_attributes.label :file, "File:" %>
    <%= uploads_attributes.file_field :file %>

    <%= uploads_attributes.hidden_field :status, value: "raw" %>
<% end %>

Problem

This solution works when I want to upload only one file. But if I want to upload two files I can't figure out. I can add multiple: true to the file_field but how to created multiple Upload with one file each ?

Should I save the uploaded files into a temp variable first, extract them from the intervention_params, then create the Intervention without any Upload, then for each uploaded files saved, build a new Upload for the newly created Intervention ?


Solution

  • I have done what I have proposed. I don't know if there is a more elegant way to do it but anyway, this is working.

    In my form I just added a simple files field that can take multiple files

    <div class="field">
      <%= form.label :files %>
      <%= form.file_field :files, multiple: true %>
    </div>
    

    I have added this to the permitted parameters : params.require(:intervention).permit(:user_id, :comment, files:[]])

    Then I create my Intervention by ignoring this files params and I use it later to create a new Upload record for each files submitted.

    # First create the intervention without the files attached
    @intervention = Intervention.new(intervention_params.except(:files))
    
    if @intervention.save
      # Then for each files attached, create a separate Upload
      intervention_params[:files].each do |f|
        upload = @intervention.uploads.new()
        upload.file.attach(f)
        upload.save
      end
    end