ActiveStorage: проверка, если .attached? для коллекции, в которой предварительно загружены вложения (избегая n + 1 запросов метода attach?) - PullRequest
0 голосов
/ 09 октября 2019

В моем приложении на Rails 5, создание JSON API и использование Activestorage для моих вложений:

Чтобы избежать доступа к вложению, которое не существует, я использую это если условие, как показано в этомответ но, к сожалению, это, если условие заставляет SQL-запрос проверить доступность вложения или нет, что вызывает n + 1 запрос, хотя я уже включил (предварительно загрузил) вложения для записей, которые у меня есть

Как проверить, существуют ли вложения в записи, не обращаясь к БД, если я уже предварительно загрузил вложения?

Я пытался использовать .nil? но это даст ложный положительный результат, что есть вложение, даже если нет (как показано ниже)

ActionView::Template::Error (to_model delegated to attachment, but attachment is nil):
  if !product.logo.nil?
    json.logo_img url_for(product.logo)

.attached? проверка работает, но вызывает n + 1 запросов:

  if product.logo.attached?
    json.logo_img url_for(product.logo)

Так что, хотя приведенный ниже код выглядит очень некрасиво, он работает, предварительно загружая вложения для всех записей одновременно, но при проверке, существует ли вложение =>, это приводит к n + 1 запросам

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

....

Будем благодарны за любые мысли о том, как правильно это делать (предварительная загрузка вложений и обход n + 1).

Обновление:

Я хочуиметь всю коллекцию, которую я передал, , включая элементы с и без вложения , в поиске условия, которое проверит, есть ли у уже загруженного элемента вложение или нет, чтобы отобразить изображение, если оноимеет вложение

Ответы [ 3 ]

0 голосов
/ 09 октября 2019

Если я вас правильно понимаю, вы можете сделать что-то вроде этого.

В вашей модели:

class Product < ApplicationRecord
  has_one_attached :logo
  scope :with_eager_loaded_logo, -> { eager_load(logo_attachment: :blob) }
end

Затем в вашем контроллере, например:

def index
  @products = current_user.products.with_eager_loaded_logo
end

Не JSON, но все же некоторый пример просмотра (HTML):

<%= image_tag (product.logo) if product.logo.attached? %>

Подробнее в этом посте .

0 голосов
/ 09 октября 2019

Rails генерирует метод сбора для has_one_attached отношений. Вы можете просто сделать что-то вроде этого:

Product.all.with_attached_logo.map{|product| product.logo.attached? }

, который сделает один загруженный запрос.

Допустим, вы хотите показать логотипы каждого продукта внутри views/products/index.htm.erb

Итак, давайте настроим переменную @products в действии index вашего controllers/products_controller.rb

def index
  @products = Product.all.with_attached_logo
end

. Это приведет к загрузке вложения логотипа в каждый продукт, если он есть. Теперь мы можем создать страницу индекса товаров без проблемы N + 1, например:

<% @product.each do |product| %>
  <% if product.logo.attached? %>
    <%= image_tag product.logo %>
  <% else %>
    <%= image_tag "product_logo_placeholder.jpg" %>
  <% end %>
  ... the rest of the product's information ...
<% end %>
0 голосов
/ 09 октября 2019

Проблема оказалась в том, что сложный includes, который у меня был, на самом деле не был предварительной загрузкой вложений (это была предварительная загрузка для вложенной записи, но не основной), поэтому метод .attached? делал запрос SQL кcheck.

Однако, когда правильная предварительная загрузка выполнена, .attached? работает правильно, проверяя объект в памяти без запуска отдельного запроса SQL для повторной проверки

Я оставляю вопросдля ознакомления сообщества, если кто-то сталкивается с подобной проблемой.

...