In my Rails 5 application, building a JSON API & using Activestorage for my attachments:
To avoid accessing an attachment that doesn't exist, I am using this if condition as shown in this answer but unfortunately this if condition makes a SQL query to check if the attachment is available or not, causing n+1 queries although I have already included (pre-loaded) the attachments for the records I have
How to check if the attachment exist on a record without hitting the DB, if I have preloaded the attachments already?
I tried to use .nil? but that will give a false positive that there is attachment even if there isn't (as shown below)
ActionView::Template::Error (to_model delegated to attachment, but attachment is nil):
if !product.logo.nil?
json.logo_img url_for(product.logo)
.attached?
check works but causes the n+1 queries:
if product.logo.attached?
json.logo_img url_for(product.logo)
So while the below code looks very ugly, it works, preloading the attachments for all the records at once, but when checking if an attachment exists => this leads to n+1 queries
json.array!(@some_records.includes(integrated_product: [company: [ logo_attachment: :blob ]])) do |one_record|
product ||= one_record.integrated_product
if !product.logo.attached?
json.logo_img url_for(product.logo)
elsif !product.company.logo.attached?
json.logo_img url_for(product.company.logo)
end
....
Any thoughts on how to handle this in a proper way (preloading attachments & avoiding n+1) are appreciated.
Update:
I want to have the whole collection that I passed, including items with & without attachment, searching for a condition that will check if that already loaded item has an attachment or not to display an image if it has attachment
The problem turned out to be that the complex includes
I had was not actually preloading the attachments (it was preloading for the nested record but not the main one), therefore the .attached?
method was making a SQL query to check.
However, when the correct preloading is done, the .attached?
works correctly, checking the in-memory object without firing a seperate SQL query to check again
I am leaving the question for the reference of the community if someone face a similar issue.