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

ActiveAdmin中如何将Survey编辑的Update动作替换为Create动作?

解决ActiveAdmin编辑Survey时创建新对象而非更新原对象的问题

你的思路方向是对的——重写ActiveAdmin的update动作来生成新对象,但直接调用create(options = survey_hash)不符合ActiveAdmin和Rails的控制器逻辑,这就是为什么没成功。下面我会一步步告诉你怎么正确实现这个需求:

核心思路

我们需要完全替换默认的update行为:找到原Survey对象,复制它的属性(排除唯一标识和时间戳),结合用户编辑后的参数,创建一个全新的Survey并关联当前AdminUser,最后保存新对象而不修改原对象。

完整实现代码

首先确保你的ActiveAdmin配置里正确设置了强参数(包括嵌套的sections_attributes),然后重写控制器的update方法:

ActiveAdmin.register Survey do
  # 配置允许的参数,根据你的Survey模型实际属性调整
  permit_params :title, :description, # 替换成你Survey的真实属性
                sections_attributes: [:id, :title, :_destroy, 
                                      question_sets_attributes: [:id, :title, :_destroy,
                                                                questions_attributes: [:id, :content, :_destroy]]]

  controller do
    def update
      # 获取原Survey对象
      original_survey = Survey.find(params[:id])
      
      # 构建新Survey的属性:复制原对象属性,用编辑后的参数覆盖,排除不需要的字段
      new_survey_attrs = original_survey.attributes.except('id', 'created_at', 'updated_at', 'admin_user_id')
                                                  .merge(params[:survey].permit(*permitted_params).to_h)
      
      # 关联当前登录的AdminUser,创建新对象
      new_survey = current_admin_user.surveys.build(new_survey_attrs)

      # 可选:如果需要完全复制原Survey的sections(忽略编辑的内容)
      # 取消下面注释并删除上面merge里的sections_attributes部分
      # new_survey.sections_attributes = original_survey.sections.map do |section|
      #   section.attributes.except('id', 'created_at', 'updated_at', 'survey_id')
      # end

      # 保存新对象并处理结果
      if new_survey.save
        redirect_to admin_survey_path(new_survey), notice: "新Survey已创建成功!"
      else
        # 保存失败时回到编辑页面显示错误
        render :edit, locals: { page: active_admin_config.get_page(:edit).new(assigns) }
      end
    end
  end
end

关键细节解释

  1. 强参数必须配置正确:如果你的Survey有嵌套属性(比如sections),一定要在permit_params里声明,否则提交的嵌套参数会被过滤,导致新对象缺少关联数据。
  2. 属性复制逻辑:用original_survey.attributes.except(...)排除id、时间戳和admin_user_id——这些字段要么自动生成,要么由当前用户关联,不需要从原对象复制。
  3. 关联处理:通过current_admin_user.surveys.build创建新对象,确保新Survey和当前登录的AdminUser正确关联,匹配你的模型belongs_to :admin_user的设定。
  4. 嵌套属性的两种选择:
    • 若希望新Survey使用用户编辑后的sections内容,保留代码里的merge(params[:survey]...)逻辑;
    • 若希望完全复制原Survey的sections(忽略编辑内容),取消注释那段遍历sections的代码。

更友好的替代方案(可选)

如果你不想替换默认的update动作(避免用户误操作),可以添加一个单独的“复制并保存”按钮,让用户主动选择是否创建新对象:

ActiveAdmin.register Survey do
  # ... 保留原有的permit_params和基础配置

  # 在Survey详情页添加复制按钮
  action_item :duplicate, only: :show do
    link_to "复制Survey", duplicate_admin_survey_path(survey), method: :post
  end

  # 定义复制动作
  member_action :duplicate, method: :post do
    original_survey = Survey.find(params[:id])
    new_survey = original_survey.dup
    # 深度复制关联的sections
    new_survey.sections = original_survey.sections.map(&:dup)
    new_survey.admin_user = current_admin_user

    if new_survey.save
      redirect_to admin_survey_path(new_survey), notice: "Survey复制成功!"
    else
      redirect_to admin_survey_path(original_survey), alert: "复制失败:#{new_survey.errors.full_messages.join(', ')}"
    end
  end
end

这种方式下,默认的“更新”按钮仍然修改原对象,而“复制Survey”按钮生成新对象,更符合常规操作习惯。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:05:19