Level Up Your Rails Forms with Active Storage
Author's Note: This post is related to the Rails Fitness application that I started earlier this summer. I wrote a multi-post series about building this app and you can read the prelude to Part 1 here .
While reviewing the code, I decided I wanted a user to be able to upload images to go with a routine and/ or its exercises. To achieve this goal, I decided to configure image uploads and attachments through Active Storage.
What Is Active Storage?
Active Storage is a component of Rails that lets a user upload and attach one or more files to an Active Record object.
More specifically, Active Storage lets you:
- Transform image uploads via ImageMagick
- Render non-image files such as PDF and video
The only real requirement for setting up Active Storage properly is for the version of Rails to be 5.2 or higher. Other than that, the setup process is pretty simple.
1. Set up the migrations
bundle exec rails active_storage:install, then
bundle exec rails db:migrate and take a look at the following code added to
create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false t.integer "record_id", null: false t.integer "blob_id", null: false t.datetime "created_at", null: false t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true end create_table "active_storage_blobs", force: :cascade do |t| t.string "key", null: false t.string "filename", null: false t.string "content_type" t.text "metadata" t.bigint "byte_size", null: false t.string "checksum", null: false t.datetime "created_at", null: false t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true end
Two new tables are created afterward. The first,
active_storage_attachments, according the Rails Guides article on Active Storage, is a "polymorphic join table that stores your model's class name". It is the connection between the model at hand and the other table.
activestorage_blob contains all of the key metadata pertaining to the uploaded file. The "blob" stores information such as
2. Attach the file(s) to a model
Now that we've migrated the necessary tables related to Active Storage, we need to set up the mappings between a given record and the file(s) that might be attached to it. There are two distinct ways to set up attachments in models
has_one_attached, we are establishing a one-to-one connection between an Active Record object and a single file (represented as an instance of
class Exercise < ApplicationRecord has_many :routine_exercises, dependent: :destroy has_many :routines, through: :routine_exercises has_one_attached :image def exercises_attributes=(exercise) if exercise[:name] != "" && exercise[:exercise_type] != "" && exercise[:description] != "" new_exercise = self.exercises.build new_exercise.name = exercise[:name] new_exercise.exercise_type = exercise[:exercise_type] new_exercise.description = exercise[:description] new_exercise.image.attach(exercise[:image]) # ^^^ end end end
^^^If there are custom attribute setters (like in the snippet above), then the file(s) needs to be attached to new instance being built.
has_many_attached, we are setting up a many-to-many connection between the Active Record object and multiple files (represented altogether as an instance of
3. Incorporate Active Storage in controllers
If you're creating and updating records with strong params, then the Active Storage instance variable must be added in the list of permitted params attributes.
class ExercisesController extends ApplicationController . . . private # in this example, `image` is the AS variable we're adding to the strong params def exercise_params params.require(:exercise).permit(:id, :name, :exercise_type, :description, :image, :routine_exercises_attributes => [:id, :routine_id, :sets, :reps]) end end
If you're not using strong params, then the process of passing the Active Storage variable in the controller is taken care of. (Make sure it's attached, though! -->
4. Add a
file_field in the new/edit views
You'll then want to incorporate uploading images via Active Storage in the actual forms. Whether the form is inside the actual views or separated into a partial, you'll want to add code similar to this:
# app/views/exercises/_form.erb # inside the form form.file_field(:image)
See the code for an actual example.
When the server is started, the rendered form will look something like this:
Do you notice the new button for uploading an image? We've successfully set up Active Storage!
After setting up, actually using Active Storage is a breeze. If we were to fill out a routine/ edit form like normal (except we're now uploading images) and submit, the new routine looks like this:
Cost/ Benefit Analysis
The Pros: Active Storage is the official Rails tool used for attaching files to Active Record models.
With the help of the special
active_storage_attachments tables, we can attach a file to a model without adding a separate column to the model's table itself.
The Cons: As of right now, I haven't really found any cons for Active Storage!
If you're looking fora quick way to upload images to go with your Active Record models in your Rails projects, this is the solution for you. Active Storage is easy to set up and implement. It's a tool I'll definitely keep close to me.
Happy Coding! Brandon