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

