Techioz Blog

Rails activestorageを使用して複数のファイルをアップロードする適切な方法は何ですか

概要

複数の「サポート写真」ファイルをアイテムモデルにアップロードしようとしています。

複数のファイルをアップロードする必要があるため、サポートする写真ファイルには has_many_attached の関連付けがあります。

item.rb

class Item < ApplicationRecord
    belongs_to :user
   

    has_one_attached :cover_photo do |attachable|
        attachable.variant :medium, resize_to_limit:[300,300]
        attachable.variant :large, resize_to_limit:[1200,1000]
    end

    has_many_attached :supporting_photos do |attachable|
        attachable.variant :medium, resize_to_limit:[300,300]
        attachable.variant :large, resize_to_limit:[1200,1000]
    end

ユーザーが記入するフォームには、file_field タグを持つ複数の div があり、ユーザーが複数の「サポート写真」を添付できるようにします。

items/new.html.erb

  <div class="mt-2 flex justify-center rounded-lg border border-dashed border-gray-900/25 px-6 py-10">
                    <div class="text-center">
                      
                      <div class="mt-4 flex text-sm leading-6 text-gray-600">
                      
                       <%= form.file_field :supporting_photos, direct_upload:true %>
                   
                      </div>
                       <p class="text-xs leading-5 text-gray-600">or drag and drop</p>
                      <p class="text-xs leading-5 text-gray-600">PNG, JPG, GIF up to 10MB</p>
                    </div>
                  </div>
                </div>
                <div  class="sm:col-span-2">
                
                  <div class="mt-2 flex justify-center rounded-lg border border-dashed border-gray-900/25 px-6 py-10">
                    <div class="text-center">
                      
                      <div class="mt-4 flex text-sm leading-6 text-gray-600">
                
                      <%= form.file_field :supporting_photos, direct_upload:true %>
                   
                      </div>
                       <p class="text-xs leading-5 text-gray-600">or drag and drop</p>
                      <p class="text-xs leading-5 text-gray-600">PNG, JPG, GIF up to 10MB</p>
                    </div>
                  </div>
                </div>

                <div  class="sm:col-span-2">
                 
                  <div class="mt-2 flex justify-center rounded-lg border border-dashed border-gray-900/25 px-6 py-10">
                    <div class="text-center">
                      
                      <div class="mt-4 flex text-sm leading-6 text-gray-600">
                
                        <%= form.file_field :supporting_photos, direct_upload:true %>
                   
                      </div>
                       <p class="text-xs leading-5 text-gray-600">or drag and drop</p>
                      <p class="text-xs leading-5 text-gray-600">PNG, JPG, GIF up to 10MB</p>
                    </div>
                  </div>
                </div>

最後に、これが私のアイテムコントローラーです。これを行う方法を調べたとき、これが私が見つけた解決策でしたが、「許可されていないパラメーター: :supporting_photos」というエラーが発生しました。コンテキスト: { コントローラー: ItemsController、アクション: create、リクエスト: #<ActionDispatch…’ ターミナル コンソールで、フォームを読み込むときにブラウザーで #<actiondispatch の未定義メソッドもそれぞれ’

誰かこれを行う適切な方法を知っていますか?お願いします、そしてありがとう

def new
    # @item = item.new
    @item = current_user.items.build
  end

  def create
    # @item = item.new(item_params)
    @item = current_user.items.build(item_params.except(:supporting_photos))
    supporting_photos = params[:item][:supporting_photos]


    if supporting_photos
      supporting_photos.each do |supporting_photo|
        @item.supporting_photos.attach(supporting_photo)
      end
    end
    
    if @item.save
      
      redirect_to sell_sale_path
    else
      render :new, status: :unprocessable_entity
    end
  end


  private
    def item_params
      params.require(:item).permit(:name, :brand, :colour, :condition, :department, :category, :price, :is_sold, :cover_photo, :description, supporting_photos: [])
    end

ユーザーにとってより直感的なものにしたいので、複数のファイルフィールドも必要です これが私が目指していることです

これとは対照的に

解決策

複数のファイルフィールドは必要ありません。複数の true 属性を持つ 1 つだけ

また、許可するには、このパラメータを :item キーでラップする必要があることにも注意してください。基本的に、この目的のために :model キーとともに form_with を使用することが可能です。

<%#= app/views/items/new.html.erb %>

<%= render "form", item: @item %>
<%#= app/views/items/_form.html.erb %>

<%= form_with(model: item) do |form| %>
  <%= form.file_field :supporting_photos, direct_upload: true, multiple: true %>

  <%= form.submit %>
<% end %>

ただし、アイテムを更新するときに既存の写真を保持し、このフォームを部分的に再利用する必要がある場合は、複数の非表示フィールドを追加する必要がある場合があります。

<% item.supporting_photos.each do |sp| %>
  <%= form.hidden_field :supporting_photos, multiple: true, value: sp.signed_id %>
<% end %>

バックエンド側では、明示的に写真を添付したり (特に保存前に) パラメータを反復したりする必要はなく、十分です。

# app/controllers/items_controller.rb

def create
  @item = Item.new(item_params)

  if @item.save
    redirect_to item_url(@item)
  else
    render :new, status: :unprocessable_entity
  end
end

private

def item_params
  params.require(:item).permit(supporting_photos: [])
end