We have our app using uuids are primary keys, on a Postgresql Database. (Standard setup described here).
We integrated ActiveStorage following the process described here. A standard setup using rails active_storage:install
and migrated using rails db:migrate
.
We have a model & corresponding controller as follows:
# Model
class Message < ApplicationRecord
has_one_attached :image
def filename
image&.attachment&.blob&.filename
end
end
# Controller
class MessagesController < ApplicationController
def create
message = Message.create!(message_params)
redirect_to message
end
private
def message_params
params.require(:message).permit(:title, :content, :image)
end
end
We observed that the first few sets of image were correctly associated with the model instances, but then we used to get random images for model instance, or got no image at all. Every time, we restart the server, we get first few images right, but then it was unpredictable.
Unsure, of what's going wrong, we debugged in rails console:
params[:image]
=> #<ActionDispatch::Http::UploadedFile:0x007fcf2fa97b70 @tempfile=#<Tempfile:/var/folders/dt/05ncjr6s52ggc4bk6fs521qw0000gn/T/RackMultipart20180726-8503-vg36kz.pdf>, @original_filename="sample.pdf", @content_type="application/pdf", @headers="Content-Disposition: form-data; name=\"file\"; filename=\"sample.pdf\"\r\nContent-Type: application/pdf\r\n">
On saving the instance and retrieving file name we got a random file, we uploaded previously.
@message = Message.new(message_params)
@message.filename
=> #<ActiveStorage::Filename:0x007fcf32cfd9e8 @filename="sample.pdf">
@message.save
@message.filename
=> #<ActiveStorage::Filename:0x007f82f2ad4ef0 @filename="OtherSamplePdf.pdf">
Looking for an explanation for this weird behaviour, and a possible solution too.
I'm late to the party hitting this in 2020 but as anurag mentioned this is due to the active_storage_attachments
DB table using a bigint for the record_id
. I wasn't able to migrate all the models with ActiveStorage attachments to use UUIDs so I needed a way to support both UUIDs and bigints at the same time.
Warning: If you can avoid this (by migrating everything to UUID most likely) then I'd strongly recommend doing that, and I plan on doing that as soon as we have time.
Warnings aside, migrating the active_storage_attachments table to change the record_id column to be text
does work. I had to adjust the few places in our app where we were joining against the active_storage_attachments
table using record_id
to cast the value to text in the join. For example I used the following code when joining to a model that had a UUID ID.
.joins("
LEFT OUTER JOIN active_storage_attachments
ON active_storage_attachments.record_id = documents.id::text
")
Hopefully this helps someone else who is stuck in the halfway state where not all ActiveStorage using models are using UUIDs or bigint IDs.
After hours of going line by line in the activestorage source code, and running the same commands
@message = Message.new(message_params)
@message.save
again and again. We got same random results again and again. Then we went through the logs rails printed while attaching the image to message and observed the following:
S3 Storage (363.4ms) Uploaded file to key: KBKeHJARTjnsVjkgSbbii4Bz (checksum: S0GjR1EyvYYbMKh44wqlag==)
ActiveStorage::Blob Create (0.4ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["key", "KBKeHJARTjnsVjkgSbbii4Bz"], ["filename", "sample.pdf"], ["content_type", "application/pdf"], ["metadata", "{\"identified\":true}"], ["byte_size", 3028], ["checksum", "S0GjR1EyvYYbMKh44wqlag=="], ["created_at", "2018-07-26 04:54:33.029769"]]
ActiveStorage::Attachment Create (2.7ms) INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["name", "file"], ["record_type", "Message"], ["record_id", "534736"], ["blob_id", "0"], ["created_at", "2018-07-26 05:04:35.958831"]]
record_id
was being set as 534736
, instead of an uuid. Here's where we went wrong.
Active storage was expecting integer foreign key to our Message model, and we wanted it to use uuids instead. So we had to fix our migration, to use uuids instead of integer foreign keys.
Solution:
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs, id: :uuid 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 ], unique: true
end
create_table :active_storage_attachments, id: :uuid do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
t.references :blob, null: false, type: :uuid
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
end
end
end
Hope this helps, someone facing similar issues. cheers!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With