ruby-on-railsrails-activestorage

Rails Active Storage - Keep Existing Files / Uploads?


I have a Rails model with:

has_many_attached :files

When uploading via Active Storage by default if you upload new files it deletes all the existing uploads and replaces them with the new ones.

I have a controller hack from this which is less than desirable for many reasons:

What is the correct way to update images with has_many_attached in Rails 6

Is there a way to configure Active Storage to keep the existing ones?


Solution

  • Looks like there is a configuration that does exactly that

    config.active_storage.replace_on_assign_to_many = false
    

    Unfortunately it is deprecated according to current rails source code and it will be removed in Rails 7.1

    config.active_storage.replace_on_assign_to_many is deprecated and will be removed in Rails 7.1. Make sure that your code works well with config.active_storage.replace_on_assign_to_many set to true before upgrading. To append new attachables to the Active Storage association, prefer using attach. Using association setter would result in purging the existing attached attachments and replacing them with new ones.

    It looks like explicite usage of attach will be the only way forward.

    So one way is to set everything in the controller:

    def update
      ...
      if model.update(model_params)
        model.files.attach(params[:model][:files]) if params.dig(:model, :files).present?
      else
        ...
      end
    end
    

    If you don't like to have this code in controller. You can for example override default setter for the model eg like this:

    class Model < ApplicationModel
      has_many_attached :files
    
      def files=(attachables)
        files.attach(attachables)
      end
    end
    

    Not sure if I'd suggest this solution. I'd prefer to add new method just for appending files:

    class Model < ApplicationModel
      has_many_attached :files
    
      def append_files=(attachables)
        files.attach(attachables)
      end
    end
    

    and in your form use

      <%= f.file_field :append_files %>
    

    It might need also a reader in the model and probably a better name, but it should demonstrate the concept.