Rails 6 更新操作中嵌套属性不删除现有记录的问题如何解决
方案1:手动补全_destroy标记删除未提交的嵌套记录
这是改动最小的兼容嵌套属性原生逻辑的方案:
- 先修改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
- 修改控制器的参数许可,增加
_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
相关产品推荐
相关产品推荐

