Saves files and attachments regardless of errors in the model I have a long form with dozens of fields, and users can upload photos and attachments in the form. It's super annoying when there is a simple field missing, the record doesn't save, and the user has to go and pick &amp; choose all the files again. So I save the photos and attachments even upon validation errors. I would have liked to use asynchronous Javascript uploading, but that requires a separate form, and forms cannot be embedded in other forms. 

This works, but it doesn't feel very DRY or very Rails. Is there a way to improve it?

class User &lt; ApplicationRecord
  belongs_to :image, optional: true, dependent: :destroy
  belongs_to :company, optional: true, dependent: :destroy
  accepts_nested_attributes_for :company, :image

class Company &lt; ApplicationRecord
  belongs_to :image, optional: true, dependent: :destroy
  has_and_belongs_to_many :attachments, dependent: :destroy
  accepts_nested_attributes_for :image, :attachments, :principals, reject_if: :all_blank, allow_destroy: true

class UsersController &lt; ApplicationController
  def update
    @user = User.find(params[:id])
    @user.assign_attributes user_params # has to come before update_* or else it will create company twice for new users
    update_image
    update_company_image
    update_company_attachments
    if @user.save 

  def update_image
    if image_params.has_key? :file
      if @user.image
        @user.image.update image_params
      else
        @user.create_image image_params
        @user.save(validate: false)
      end
    end
  end

  def update_company_image
    if company_image_params.has_key? :file
      if @user.company &amp;&amp; @user.company.image # company &amp; image exists
        @user.company.image.update company_image_params
      elsif @user.company # company exists
        @user.company.create_image company_image_params
        @user.company.save(validate: false)
      else
        @user.build_company
        @user.company.create_image company_image_params
        @user.company.save(validate: false)
        @user.save(validate: false)        
      end
    end
  end

  def update_company_attachments
    company_attachments_params.each do |cap|
      if cap.has_key? :file
        if @user.company
          @user.company.attachments.create cap
        else
          @user.build_company
          @user.company.attachments.create cap
          @user.company.save(validate: false)
          @user.save(validate: false)
        end
      end
    end
  end

  def user_params
    params.require(:user).permit :name, ..., 
      company_attributes: [:id, :name, ...,
        attachments_attributes: [:id, :_destroy]]
        # image_attributes: [:id, :file],
        # attachments_attributes: [:id, :file, :_destroy]]
      #image_attributes: [:id, :file]
  end

  def image_params
    params.require(:user).fetch(:image_attributes, {}).permit :id, :file
  end

  def company_image_params
    params.require(:user).fetch(:company_attributes, {}).fetch(:image_attributes, {}).permit :id, :file
  end

  def company_attachments_params
    params.require(:user).fetch(:company_attributes, {}).permit(:attachments_attributes, attachments_attributes: [:id, :file, :_destroy]).fetch(:attachments_attributes, {}).values
  end
Uploaded files are a tricky thing. You really don't want to move the files into their final resting spot until the other stuff is saved. Ideally, you want:


Commit to database
Move files to permanent storage
(Optionally) One more commit to the database to update file paths or names


Validation errors, of course, throw this all out of whack. From a UI perspective you solve this by switching your form from "create mode" to "edit mode" -- don't show the "upload" field. Show the files in the UI as if you were editing something that already existed in file storage, except you are referencing a temp file on the server instead of a file in permanent storage.

So now the process becomes:


Run validations (they fail...)
Update a temporary copy of you model with the temp file paths


This is where a "view model" can be useful, so the object used to render things on screen does not have to adhere to the same constraints as your Active Record classes

Re-render the page so the user can correct the errors
User corrects errors
User submits form, including references to temp files
Commit to database
Move files to permanent storage
(Optionally) One more commit to the database to update file paths or names