ActiveStorage issue to set record_type on active_storage_attachments
I found this problem (try in console).
If you set profile = Account::Profile.last
then call profile.avatar.attached?
it returns false. This is because the column record_type
in ActiveStorage::Attachment
is set to User.
So, you can not access the blob because profile.avatar.blob
returns the following query:
SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 1], ["record_type", "Account::Profile"], ["name", "avatar"], ["LIMIT", 1]]
And the error: Module::DelegationError: blob delegated to attachment, but attachment is nil
One possible workaround that I found is to define Account::Profile as follows:
class Account::Profile < ApplicationRecord
self.table_name = "users"
# has_one_attached :avatar # remove this
def avatar
ActiveStorage::Attachment.where(name: :avatar, record_type: 'User', record_id: self.id).last
end
end
This works for showing the image but has the problem that profile.avatar.class
is not ActiveStorage::Attached::One
(like User.last.avatar.class
) but ActiveStorage::Attachment
.So, you can not call for example .attached?
method on it. You must use profile.avatar.present?
to check if the avatar is present.
A possible better solution is to define the instance method avatar in this way:
def avatar
ActiveStorage::Attached::One.new('avatar', User.find(id), dependent: :purge_later)
end
It is required to instantiate an object of ActiveStorage::Attached::One
but the record must be User
class (to match record_type
), that's why User.find(id)
.Now all methods are available:
profile.avatar.methods
. Rails Active Storage not working every time. Some time it works, sometime it doesn't
My main problem was that I am using :uuid
for primary key for all my tables so I has to edit ActiveStorage migration to use :uuid
. I had changed mainly these 2 lines t.references :record, null: false, polymorphic: true, index: false, type: :uuid
and t.references :blob, null: false, type: :uuid
. My final migration looks like this. And after this ActiveStorage is now woking fine.
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
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
ActiveStorage not persisting blob to attachment after Rails 6.0 upgrade
After a bunch of playing around I worked out that the Active Storage relationships were not being updated unless the model was altered in some way, for example, touching the updated_at
timestamp, or another editing another field - I think this is a Rails bug.
By Adding <%= f.hidden_field :updated_at, value: DateTime.current %>
in my forms, I was able to force Active Storage to persist the file.
Hope this helps someone else.
Rails ActiveStorage scope for when file is NOT attached
You can do that using left_joins
with the name of the association (image + _attachment
) which is interpreted as:
SELECT users.*
FROM users LEFT OUTER JOIN active_storage_attachments
ON active_storage_attachments.record_id = users.id
AND active_storage_attachments.record_type = 'User'
AND active_storage_attachments.name = 'image'
And then apply a WHERE
filter to get those user rows without match against the active_storage_attachments
table:User.left_joins(:image_attachment).where(active_storage_attachments: { id: nil })
Active Storage Rails 6 API unable to insert to active_storage_attachments table
I add a new method for attachment this is my refactor code. Hope this will help.
class Api::V1::UploadController < ActiveStorage::DirectUploadsController
skip_before_action :verify_authenticity_token
def create
blob = ActiveStorage::Blob.create_before_direct_upload!(blob_args)
render json: direct_upload_json(blob)
end
def attached_create
ActiveStorage::Attachment.create(params_attached) #from react axios pass params
end
private
def params_attached
params.require(:attachment).permit(:name, :record_type, :record_id, :blob_id)
end
def blob_args
params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type, metadata: {}).to_h.symbolize_keys
end
def direct_upload_json(blob)
blob.as_json(root: false, methods: :signed_id).merge(service_url: url_for(blob)).merge(direct_upload: {
url: blob.service_url_for_direct_upload,
headers: blob.service_headers_for_direct_upload
})
end
Rails: Reducing the amount of ActiveStorage queries
Depending in your case (has_many
/has_one
) use with_attached_<attachment>
:
So:To avoid N+1 queries, you can include the attached blobs in your query
like so:
Gallery.where(user: Current.user).with_attached_photos
[
Album.all,
Demo.with_attached_images.all,
Samplepack.with_attached_images.all
]
I think there's no need to create an empty array to fill it then.There's perhaps also a problem since you're using all
, check what you really need from each model.
Related Topics
Axlsx - Formatting Text Within a Cell
Activerecord Join Table for Legacy Database
"Previous Post" and "Next Post" Link in Show View (Nested Resources)
Paperclip and Amazon S3 How to Do Paths
Deleting Items from an Array Requires Multiple Passes to Remove Them All
How to Create Thor::Group Generators as Args of My_Command
What Is Toplevel_Binding in Ruby
How to Fix Difference in Behavior of Activesupport 3.0.0 Compare to 2.X
Ruby on Rails Private Link Sharing: Google Docs Style
How to Make Devise Registrationscontroller to Show Sign_Up Page Only If User Is Already Signed In
Use a String to Access a Local Variable by Name
How to Include Actionmailer Class in Rake Task
Rendering a View from My Ruby on Rails Gem
Rails 3.1 Has_One Nested Resource: Routing Not Generating "All" Paths