List ActiveStorage attachments indirectly related to model

Viewed 1355

I am new to Rails and am trying to understand how ActiveStorage works.

The app has the following models:

class Client < ApplicationRecord
    has_many :jobs
    has_many :messages
end
class Job < ApplicationRecord
    belongs_to :client
    has_many_attached :images
end
class Message < ApplicationRecord
    belongs_to :client
    has_many_attached :images
end

In the database I can see the polymorphic relationship and I can also understand what SQL query would get me the desired results.

However I was wondering if there is an idiomatic and efficient way to retrieve all the attachments related to a client?

2 Answers

Querying attachments associated with a given record

You can query for attachments directly using the ActiveStorage::Attachment class. The record attribute is the polymorphic association which joins to the attached class.

Here’s an example

ActiveStorage::Attachment.where(record: Job.first)

Querying attachments associated with a set of records

You can also pass a set of records to the record option

ActiveStorage::Attachment.where(record: Job.all)

Querying attachments associated with multiple sets of records

You can find attachments associated with multiple sets of records using or

ActiveStorage::Attachment.where(record: Job.all).or(ActiveStorage::Attachment.where(record: Message.all))

Putting it together...

To find all the attachments associated with multiple sets of records associated with a given record

client = Client.first
ActiveStorage::Attachment.where(record: client.jobs).or(ActiveStorage::Attachment.where(record: client.messages))

It’s not pretty, but it’s efficient. All the querying is done via Active Record which means it happens in a single SQL statement and doesn’t require loading any objects into memory.

There is a way, but you would have to target a Client, then ask for their messages, iterate over each message asking for their attachments. It should look something like this:

#ask for first client and all messages
oneClient = Client.first
allImages = []
oneClient.message.all.each do |msg|
  #ask for the all images of each message
  msg.image.all.each do |img|
    img << allImages
  end
end
#repeat block for Job
oneClient.job.all.each do |jb|
  jb.image.all.each do |img|
    img << allImages
  end
end

The block above should give you the relation of one Customer to all of it's messages and all of it's Message's images.

Related