Eager Loading Active Storage Attachments
Listen to this blog post:
For several years now, I have been using the extremely versatile Shrine gem for image uploads, but for this site I decided to work with Rails' built-in ActiveStorage. My initial use case was the header image that appears above each article.
The way ActiveStorage works is that it creates a join table between the blob and the object it is attached to (a BlogPost, in this case). Blobs are stored in the active_storage_blobs table, and the join table is called active_storage_attachments.
The association is created with one of two simple methods, depending on your needs:
# blog_post.rb
The way ActiveStorage works is that it creates a join table between the blob and the object it is attached to (a BlogPost, in this case). Blobs are stored in the active_storage_blobs table, and the join table is called active_storage_attachments.
The association is created with one of two simple methods, depending on your needs:
# blog_post.rb
class BlogPost < ApplicationRecord # Either has_one_attached :header_image # or has_many_attached :header_images end
The symbol I have designated here as :header_image can actually be named anything you want, but it helps to use something that makes sense to anyone who will be reading the code later.
Once this relationship has been declared, you can call it just like it was an actual column on your table. In this case, we can call @blog_post.header_image inside of an image_tag in the view, and it will display the image we have uploaded.
The catch is that once you load @blog_post in the view, calling @blog_post.header_image requires it to fetch the image at the moment it is called. Instead, we can do better by eager loading the image. This causes the data to be pulled from the database when the record is loaded, so it is ready to be used immediately when called in the view.
This is how we do it:
# blog_posts_controller.rb
class BlogPostsController < ApplicationController def show @blog_post = BlogPost.with_attached_header_image.find(params[:id]) end end
The .with_attached_* method is some Rails magic that allows you to append whatever arbitrary name you assigned to the active_storage relation (:image_header, in this case). It is smart enough to allow you to use the multiple form, if you have declared a has_many relationship (which, in this case, would be :header_images).
This is not so important when loading a single object, such as loading the show page for a single blog post, but where it really comes into play is when you are loading multiple records, for instance an index page. Thankfully, this is just as easy as before:
# blog_posts_controller.rb
class BlogPostsController < ApplicationController def index @blog_posts = BlogPost.all.with_attached_header_image end end
Now, when your index page loads, the images load much faster.