You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Turbo Frame下移除图片后编辑内容更新失效问题求助

Turbo Frame产品编辑页移除图片后文本更新丢失问题修复

问题现象

在Turbo Frame的产品编辑页面中,修改文本内容后不刷新页面移除图片,点击Update按钮后页面刷新,文本修改内容丢失,需再次点击才能真正更新。例如修改描述为“456”并移除所有图片后点击Update,页面停留在编辑页,描述变回“123”,再次点击才生效。终端报错ActiveRecord::RecordNotFound,提示找不到指定ID的ActiveStorage::Blob。

错误原因

  1. 无效表单参数残留:移除图片时仅通过Turbo Stream删除了页面上的图片DOM元素,但对应图片的signed_id隐藏字段仍留在表单中。提交表单时,这些已被删除图片的signed_id会被发送到后端,后端尝试查找已标记删除的Blob,触发404错误,导致整个更新事务回滚,文本修改也随之失败。
  2. 异步删除的时间差:使用purge_later异步删除Blob时,移除操作发起后Blob并未立即被删除,但表单提交时仍携带该Blob的signed_id,后端验证时可能找不到Blob,引发异常。

修复方案

1. 移除图片时同步删除对应隐藏字段

将图片的隐藏字段放到图片的DOM容器内,这样Turbo Stream移除容器时会一并删除隐藏字段,避免提交无效参数:

<div>
  <%= form.file_field :pictures, multiple: true %>
  <% if @product.pictures.attached? %>
    <% @product.pictures.each do |picture| %>
      <div id="<%= dom_id(picture, :picture) %>">
        <%= link_to image_tag(product.picture_as_thumbnail(picture)), picture %>
        <%= link_to "remove picture", product_picture_path(@product, blob_id: picture.blob_id), data: { turbo_method: :delete} %>
        <!-- 将hidden_field移入图片容器内 -->
        <%= form.hidden_field :pictures, multiple: true, value: picture.signed_id %>
      </div>
    <% end %>
  <% end %>
</div>

2. 同步删除Blob或过滤无效参数

方案A:同步删除Blob

将purge_later改为purge,确保Blob立即被删除,避免参数校验时出现找不到的情况:

class Products::PicturesController < ApplicationController
  include ActionView::RecordIdentifier
  before_action :set_product

  def destroy
    @picture = @product.pictures.find(params[:blob_id])
    @picture.purge # 替换purge_later为purge
    respond_to do |format|
      format.html { redirect_to edit_product_path(@product) }
      format.turbo_stream { render turbo_stream: turbo_stream.remove(dom_id(@picture, :picture))}
    end
  end

  private

  def set_product
    @product = Product.find(params[:product_id])
  end
end

方案B:过滤无效的signed_id

如果坚持使用异步删除,在后端更新时过滤掉空值和无效的signed_id,避免触发异常:

class ProductsController < ApplicationController
  # ... 其他代码 ...

  def update
    filtered_params = filter_invalid_picture_params(product_params)
    if @product.update(filtered_params)
      redirect_to @product, notice: "产品更新成功"
    else
      render :edit
    end
  end

  private

  def product_params
    params.require(:product).permit(:name, :description, pictures: [])
  end

  def filter_invalid_picture_params(params)
    return params unless params[:pictures].present?

    valid_pictures = params[:pictures].reject(&:blank?).select do |signed_id|
      ActiveStorage::Blob.find_signed(signed_id) rescue false
    end
    params.merge(pictures: valid_pictures)
  end
end

3. 捕获异常避免页面回滚

在ProductsController的update方法中捕获Blob找不到的异常,返回正确的响应,保留用户输入的文本内容:

def update
  begin
    if @product.update(filtered_params)
      redirect_to @product, notice: "产品更新成功"
    else
      render :edit
    end
  rescue ActiveRecord::RecordNotFound => e
    flash.now[:alert] = "部分图片已被删除,请重新提交"
    render :edit, status: :unprocessable_entity
  end
end

内容的提问来源于stack exchange,提问作者swaeetpotato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 03:54:56