ActiveStorage:检查是否.attached?对于已预加载附件的集合(同时避免 n+1 查询 attached? 方法)

ActiveStorage: checking if .attached? for a collection that has attachments preloaded (while avoiding n+1 queries of attached? method)

在我的 Rails 5 应用程序中,构建 JSON API 并为我的附件使用 Activestorage:

为了避免访问不存在的附件,我使用了这个 但不幸的是,这个 if 条件进行了一个 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)的想法都将受到赞赏。

更新:

我想要我通过的整个集合,包括项目 有和没有 附件,搜索将检查的条件如果已加载的项目有附件或如果有附件则不显示图像

问题原来是我的复杂 includes 实际上并没有预加载附件(它是为嵌套记录而不是主要记录预加载),因此 .attached? 方法正在进行 SQL 查询以进行检查。

但是,当完成正确的预加载后,.attached? 会正常工作,检查内存中的对象而无需触发单独的 SQL 查询再次检查

如果有人遇到类似问题,我将问题留给社区参考。

如果我理解正确的话,你可能想做这样的事情。

在您的模型中:

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? %>

更多内容见this post

Rails 为has_one_attached 关系生成集合方法。你可以这样做:

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

这将进行单个预先加载的查询。

假设您想在 views/products/index.htm.erb

中显示每个产品的徽标

那么让我们在 controllers/products_controller.rb

index 操作中设置 @products 变量
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 %>