Motivation / Background
My application retrofits a legacy database schema which uses a mixture of integer and UUID primary keys for models. Because the foreign key from ActiveStorage::Blob
to a given model must be fixed as it is a polymorphic relationship, this causes problems when attempting to attach blobs to models which do not adhere to the expected foreign key type for their primary key.
The suboptimal workaround that I’ve been doing is to create a join table between the model with the incorrect primary key type and ActiveStorage blobs; however, as usage of ActiveStorage increases its leading to a pattern I really dislike.
The ability to override the primary_key
attribute of the has_one :"#{name}_attachment"
and has_many :"#{name}_attachments"
relationships would make this far simpler.
Detail
This Pull Request changes the has_one_attached
and has_many_attached
macros to support optional attachment_primary_key
and attachments_primary_key
keyword arguments (respectively) for customizing the primary_key
attribute of has_one :"#{name}_attachment"
and has_many :"#{name}_attachments"
relationships.
Example:
class Application < Rails::Application
config.generators do |g|
g.orm(:active_record, primary_key_type: :uuid)
end
end
# == Schema Information
#
# Table name: users
#
# id :uuid not null, primary key
# name :string(50)
#
class User < ApplicationRecord
has_one_attached :profile_picture
end
# == Schema Information
#
# Table name: posts
#
# id :integer not null, primary key
# title :string(50)
# post_id :uuid <-- newly created column for compatibility with ActiveStorage::Blob
#
class Post < ApplicationRecord
has_many_attached :images, attachments_primary_key: "post_id"
end
User.eager_load(:profile_picture_attachment).first
SELECT
"users"."id" AS t0_r0,
"users"."name" AS t0_r1,
"active_storage_attachments"."id" AS t1_r0,
"active_storage_attachments"."name" AS t1_r1,
"active_storage_attachments"."record_type" AS t1_r2,
"active_storage_attachments"."record_id" AS t1_r3,
"active_storage_attachments"."blob_id" AS t1_r4,
"active_storage_attachments"."created_at" AS t1_r5
FROM "users"
LEFT OUTER JOIN "active_storage_attachments"
ON "active_storage_attachments"."record_type" = ?
AND "active_storage_attachments"."name" = ?
AND "active_storage_attachments"."record_id" = "users"."id" # Existing behavior
ORDER BY "users"."id" ASC
LIMIT ?
> Post.eager_load(:images_attachments).first
SELECT
"posts"."id" AS t0_r0,
"posts"."title" AS t0_r1,
"posts"."post_id" AS t0_r2,
"posts"."user_id" AS t0_r3,
"active_storage_attachments"."id" AS t1_r0,
"active_storage_attachments"."name" AS t1_r1,
"active_storage_attachments"."record_type" AS t1_r2,
"active_storage_attachments"."record_id" AS t1_r3,
"active_storage_attachments"."blob_id" AS t1_r4,
"active_storage_attachments"."created_at" AS t1_r5
FROM "posts"
LEFT OUTER JOIN "active_storage_attachments"
ON "active_storage_attachments"."record_type" = ?
AND "active_storage_attachments"."name" = ?
AND "active_storage_attachments"."record_id" = "posts"."post_id" # New column override
WHERE "posts"."id" = ?
ORDER BY "posts"."id" ASC
- Is this something that would be acceptable?
- I’m actively working on getting the Rails testsuite executing locally; however, am concerned that I don’t fully understand the structure of the ActiveStorage testsuite well enough to write a adequate tests for this.