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

Rails 6 更新操作中嵌套属性不删除现有记录的问题如何解决

方案1:手动补全_destroy标记删除未提交的嵌套记录

这是改动最小的兼容嵌套属性原生逻辑的方案:

  1. 先修改Question模型,开启嵌套属性的删除权限:
class Question < ApplicationRecord
  belongs_to :quiz
  has_many :options

  validates :body, presence: true
  # 新增allow_destroy: true 允许嵌套属性删除记录
  accepts_nested_attributes_for :options, allow_destroy: true
end
  1. 修改控制器的参数许可,增加_destroy字段的白名单,同时在update前补全待删除记录的标记:
class QuestionsController < ApplicationController
  ...

  def update
    @question = Question.find_by(id: params[:id])
    # 获取当前问题已有的option id列表
    existing_option_ids = @question.options.pluck(:id)
    # 提取提交参数中已有的option id列表
    submitted_option_ids = question_params[:options_attributes]&.map { |opt| opt[:id]&.to_i } || []
    # 计算未出现在提交参数中的旧option id,生成销毁标记
    to_delete_ids = existing_option_ids - submitted_option_ids
    to_delete_attributes = to_delete_ids.map { |id| { id: id, _destroy: true } }
    # 合并销毁标记到提交参数中
    merged_params = question_params.merge(options_attributes: (question_params[:options_attributes] || []) + to_delete_attributes)
    
    if @question.update(merged_params)
      render status: :ok, json: { notice: t("question.successfully_updated") }
    else
      render status: :unprocessable_entity, json: { error: @question.errors.full_messages.to_sentence }
    end
  end

  ...

  private

    def question_params
      # 新增:_destroy到参数许可列表
      params.require(:question).permit(:body, :quiz_id, options_attributes: [:id, :body, :is_correct, :_destroy])
    end
end

方案2:直接替换关联集合实现完全覆盖

如果你的场景不需要保留任何未提交的旧选项,完全以提交的参数为准,直接用关联赋值的方式更简单,不需要处理嵌套属性的销毁逻辑:

def update
  @question = Question.find_by(id: params[:id])
  # 处理提交的选项参数,生成新的option集合
  new_options = params[:question][:options_attributes]&.map do |opt_params|
    permitted_opt = opt_params.permit(:id, :body, :is_correct)
    if permitted_opt[:id].present?
      # 已有记录直接查找并赋值新属性
      Option.find(permitted_opt[:id]).tap { |opt| opt.assign_attributes(permitted_opt.except(:id)) }
    else
      # 新记录直接新建
      Option.new(permitted_opt)
    end
  end || []
  # 直接给options赋值,Rails会自动删除不在新集合里的旧option记录
  @question.options = new_options

  if @question.save
    render status: :ok, json: { notice: t("question.successfully_updated") }
  else
    render status: :unprocessable_entity, json: { error: @question.errors.full_messages.to_sentence }
  end
end

这种方式完全符合「用新参数完全替换原有嵌套记录」的需求,不需要前端配合传_destroy参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 03:15:05