SunDi3yansyah
SunDi3yansyah

Reputation: 46

ActiveStorage issue to set record_type on active_storage_attachments

I have a problem for ActiveStorage

the case is I have a model named Setting::Profile and name of Admin table and attachment :avatar for uploading process succeed, but when to get the picture can not be found because at table active_storage_attachments in attribute record_type stored with name Setting::Profile, supposed with name Admin

How to add a line to prepare for the record_type attribute? Thanks

This my demo: https://github.com/SunDi3yansyah-forks/AppActiveStorage

screenshot from 2018-07-11 01-24-48

Upvotes: 3

Views: 1851

Answers (1)

iGian
iGian

Reputation: 11193

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.

Upvotes: 4

Related Questions