I'm working on a legacy Rails app, recently upgraded to Rails 5.2. It already has a roll-your-own Image implementation with uploads, and there are millions of images already in the DB. The Image
model belongs_to
other models; each of them either has_many :images
or has_one :image
.
Part of the implementation is that there's a subclass of Image
, Image::Url
, that gets the image's URL (on AWS), based on the size requested, of the various sizes already generated by ImageMagick.
So I'm considering whether Active Storage (which is not installed on this Rails app) could work with this existing setup. All the tutorials and explainers I've found discuss installing Active Storage on a new Rails app, or using it to add attachments to existing models. That's not my situation -- I already have an Image model relating to a dozen other models, and they already know what their "attachments" i.e. Image relations, are.
My question is, can Active Storage somehow make use of that existing Image table and its relations -- or is Active Storage more properly understood as an alternative to this roll-your-own setup, that cannot integrate with it.
There is an SO question about "Rails Active Storage without model" that seems to imply that a mapping between Active Storage and a model can occur. What i don't understand is the relationship betwen Active Storage and an existing Image model. As far as I understand, it doesn't make sense that the Image model would has_one_attached
or has_many_attached
(as a User or Product would have attachments) - it IS already a model of the attachment itself. Or am I getting that wrong?
CodePudding user response:
The heart of ActiveStorage is really three tables (and models) which correspond somewhat to your images table:
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
# Use Active Record's configured type for primary and foreign keys
primary_key_type, foreign_key_type = primary_and_foreign_key_types
create_table :active_storage_blobs, id: primary_key_type do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.string :service_name, null: false
t.bigint :byte_size, null: false
t.string :checksum, null: false
if connection.supports_datetime_with_precision?
t.datetime :created_at, precision: 6, null: false
else
t.datetime :created_at, null: false
end
t.index [ :key ], unique: true
end
create_table :active_storage_attachments, id: primary_key_type do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
t.references :blob, null: false, type: foreign_key_type
if connection.supports_datetime_with_precision?
t.datetime :created_at, precision: 6, null: false
else
t.datetime :created_at, null: false
end
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
create_table :active_storage_variant_records, id: primary_key_type do |t|
t.belongs_to :blob, null: false, index: false, type: foreign_key_type
t.string :variation_digest, null: false
t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
private
def primary_and_foreign_key_types
config = Rails.configuration.generators
setting = config.options[config.orm][:primary_key_type]
primary_key_type = setting || :primary_key
foreign_key_type = setting || :bigint
[primary_key_type, foreign_key_type]
end
end
As you can see from the migration it uses the active_storage_blobs
to store the actual information about the file that is stored. A blob can also have multiple variants.
active_storage_attachments
joins blobs with resources (the model attaching the attachment) through a polymorphic assocation. This lets you add has_one_attached/has_many_attached
to any model in your application without adding any additional database columns or tables.
So I'm considering whether Active Storage (which is not installed on this Rails app) could work with this existing setup.
Lets put it this way - you should not expect that you can just plug and play your legacy data into ActiveStorage. Its an extremely opinionated peice of software thats mainly designed around the goal of being able to plugged into a any number of models with a minimum of configuration.
ActiveStorage can probally work fine in tandem with your existing setup (replacing it for new records) but replacing the legacy code with AS will most likely entail some heavy data migration and you'll also need a pretty good understanding of how AS works.
What i don't understand is the relationship betwen Active Storage and an existing Image model.
Thats because there is none really. ActiveSupport::Attachment
and ActiveSupport::Blob
serve the same role for all the models in your Rails app which have attachments. Its not designed with legacy support in mind.