LoginSignup
3
0

More than 1 year has passed since last update.

ActiveStorageでN+1回避のための `with_attached_#{name}` が使えないとき

Posted at

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
  1. has_many_attached の場合は こちら

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0