How to make Rails 5.2.0rc1, ActiveStorage, and UUIDs on PostgreSQL 9+ work together with some small adjustments to the standard setup process.
What's This About?
Most of the ActiveStorage tutorials out there in the blogosphere, which have been posted in anticipation of the Rails 5.2.0rc1 stable launch, show you how to utilize ActiveStorage with the standard sequential model IDs (integers). But what if you want to use UUIDs for the unique keys on your Rails models?
In this short walkthrough, I will show you how to get Rails 5.2.0rc1, ActiveStorage, and UUIDs on PostgreSQL working together.
Installing Rails 5.2.0rc1
I don't need to repeat the same basic setups that have been written before, so I'll recommend you follow the following posts for those instructions:
Bottom line, you need to install rails with rails _5.2.0.rc1_ new app_name
and then run bundle install
for your gems
Switch from Sqlite to PostgreSQL
Next, tell Rails to utilize PostgreSQL instead of sqlite: Ruby on Rails Switch From Sqlite3 to Postgres
Checking out your Gemfile
file:
# Use sqlite3 as the database for Active Record
# gem 'sqlite3'
# Use pg, instead
gem "pg", "0.21.0"
Checking out your database.yml
file:
development: &default
adapter: postgresql
host: localhost
username: username
database: five-two_dev
pool: 5
timeout: 5000
test:
<<: *default
adapter: postgresql
username: username
database: five-two_test
Changes to ActiveStorage migrations
After you run the rails active_storage:install
command, you need to jump in and change a few things on the generated migrations before running them.
You'll need to ensure your PostgreSQL database is ready for UUIDs in the first place. Add these lines to a migration (or the generated one):
enable_extension "uuid-ossp"
enable_extension "pgcrypto"
Also, you may want to reference this post for more about the initial setup of UUIDs in your app: Rails 5.1 + Using a UUID as a primary key in ActiveRecord with PostgreSQL
Changes to Tables
Make changes to the initial setup of both active_storage_blobs
and active_storage_attachments
tables to utilize the uuid datatypes for their model id fields:
create_table :active_storage_blobs, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
...
end
create_table :active_storage_attachments, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
...
end
Do the same for your other models that will have associated ActiveStorage instances. In this case, I'm using a generic Post
model:
create_table "posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
...
end
Changes to Fields
The above changes simply cover the setup of the tables. Now we'll make changes to the actual t.references
fields which will break our UUID approach, by default:
Change your active_storage_attachments
table from this:
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false # default, problematic instruction
t.references :blob, null: false # default, problematic instruction
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
end
To this:
create_table :active_storage_attachments, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
t.string :name, null: false
t.uuid :record_id, null: false # replaces t.references :record
t.string :record_type, null: false # replaces t.references :record
t.uuid :blob_id, null: false # replaces t.references :blob
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
end
Closing Remarks
Yep, changing the migrations is mostly all there is to it. The biggest issue you're overcoming with these changes is that Rails does not understand utilizing UUIDs via the default generators, and the t.reference
helpers and will incorrectly (for our purposes) set up the polymorphic and association IDs as normal, sequential integers.
Thus, the changes above explicitly tell Rails to use UUIDs instead of the traditional IDs for the necessary associations.
Feedback
If you have problems with these instructions, please shoot me an email or create an issue on the source repo and I'll see what's up!
Notes
- The source code for my example (GitHub): wrburgess/active_storage_with_uuids
- Installing Rails 5.2 rc1
- Ruby on Rails Switch From Sqlite3 to Postgres by Dave Ferrara
- Rails 5.1 + Using a UUID as a primary key in ActiveRecord with PostgreSQL by Adam Butler at Lab.io
- Credit goes to Evil Martian's Andy Barnov and Vladimir Dementyev for their work on this post, which got me started: Rails 5.2: Active Storage and beyond
- There's derivate credit that goes to Mike Gunderloy for an earlier post on the same subject: Active Storage Samples
- The official docs - Rails Guides: Active Storage Overview
Other things to check out
- My company: All Aboard Apps
- My podcast: CTO Think Podcast
- My other podcast: This Old App Podcast