Userモデルから10件取得し、それぞれのavatarを参照する場合を考える。
何も考えずにやるとN+1クエリの問題が発生してしまう。
class User < ApplicationRecord
has_one_attached :avatar
end
# N+1クエリが発生する
User.limit(10).each do |user|
p user.avatar.attached?
end
N+1クエリを回避するためには with_attached_#{name}
という scope が使える。1
# with_attached_#{name} を使えば回避できる
User.limit(10).with_attached_avatar.each do |user|
p user.avatar.attached?
end
ただし with_attached_#{name}
は内部的には includes を使っているため、たとえばUserモデルの主キーがRails標準の id
ではなく、独自のstring型の user_id
を利用していた場合には、このスコープは使えない。
そのような場合にActiveStorageのテーブルから直接一括取得して回避する方法がある。
具体的には以下のようにする。
avatars = ActiveStorage::Attachment.where(
record_type: 'User',
name: 'avatar',
record_id: User.limit(10).ids.map(&:to_i)
).preload(:blob)
User.limit(10).each do |user|
avatar = avatars.detect { |a| a.record_id == user.id }
p avatar&.attached?
end