patorashのブログ

方向性はまだない

ActiveStorageで画像を追加アップロードする

さっきこんな記事を書きました。

patorash.hatenablog.com

今度は、更新で画像を追加しようとしたところ、上書きされてしまいました。

追加されると思っていたのでびっくりな挙動でしたが、その辺りはこのissueにあります。

github.com

Rails5までは、追加がデフォルトの動作でしたが、Rails6からは上書きがデフォルトの動作になります。

Rails5の動作と同じにしたい場合は、config/application.rbなどで、以下の設定が必要です。

config.active_storage.replace_on_assign_to_many = false

しかし、これは設定せずに、せっかくRails6で上書きがデフォルトの動作なので、それで対応したいと思います。

対応方法

実は先ほどのissueのコメントの中に対応方法がありました。

https://github.com/rails/rails/issues/35817#issuecomment-484158884

前回の記事のビューに追加で書くとすれば、こう。

.mb-3
  = f.label :images
  = f.file_field :images, direct_upload: true, multiple: true, accept: 'image/*'
  - @post.images.each do |image|
    = f.hidden_field :images, multiple: true, value: image.signed_id

signed_idを付けることで、追加アップロードにできるようになりました。

新たな不具合

しかし、新たな不具合が起きました。 ファイルを追加しつつ、既存の画像を削除しようとしたら、エラーになりました。

qiita.com

これと同じようなエラーなのですが、@post.imagesを追加してから削除して…という途中で、hidden_fieldに入れたsigned_idでは追加しようとするのに、削除フラグがONになっているからおかしくなるのでしょう。 これに関しては、スマートな回避方法を思いつきませんでした。

苦肉の策

Rails6からは上書き保存なところを利用して、削除対象のimageのsigned_idを取得して、それを上書き対象から除外しました。コントローラーのstrong_parametersでの対応となります。

class PostsController < ApplicationController
  # 略

  private

  def post_params
    permit_params = params.require(:post).permit(
        :title,
        :content,
        images: [],
        images_attachments_attributes: [ :id, :_destroy ],
    )
    images_attachments_attributes = permit_params.delete(:images_attachments_attributes)
    if images_attachments_attributes
      destroy_signed_ids = images_attachments_attributes.to_h.map do |_, attribute|
        @post.images.find_by(id: attribute[:id])&.signed_id if attribute.delete(:_destroy)
      end.compact
      permit_params[:images] -= destroy_signed_ids
    end
    permit_params
  end
end

これで、ファイルを追加しつつ、ファイルを削除可能になりました👍

まとめ

ActiveStorageは難しい…。