Rails惯用方式实现:用复选框批量选择并保存API返回的高尔夫球场数据
Rails惯用方式实现:用复选框批量选择并保存API返回的高尔夫球场数据
我太懂这种一个个点保存的痛苦了!咱们直接改成Rails惯用的批量提交方案,核心就是用单个父表单包裹所有可选球场,通过复选框标记要保存的项,再利用嵌套属性一次性提交多条数据。下面一步步来改:
第一步:给控制器加批量创建的动作
首先在Admin::GolfCoursesController里新增batch_create方法,用来处理批量提交的请求,同时做好重复校验和错误收集:
class Admin::GolfCoursesController < Admin::BaseController # 保留你原有的search、new、create方法... def batch_create # 提取用户选中的球场数据,没有选中的话就设为空数组 selected_courses = params[:golf_courses]&.values || [] saved_count = 0 error_messages = [] selected_courses.each do |course_attrs| # 后端再做一次重复校验,避免前端漏判或者用户手动篡改参数 next if GolfCourse.exists?(remote_api_id: course_attrs[:remote_api_id]) course = GolfCourse.new(course_attrs) if course.save saved_count += 1 else error_messages << "球场「#{course_attrs[:name]}」保存失败:#{course.errors.full_messages.to_sentence}" end end # 组装提示信息,兼顾成功和失败的情况 combined_notice = [] combined_notice << "成功保存 #{saved_count} 个球场" if saved_count.positive? combined_notice += error_messages if error_messages.any? if combined_notice.any? redirect_to admin_golf_courses_path, notice: combined_notice.join(";") else redirect_to admin_golf_courses_path, alert: "没有选中任何未保存的球场" end end private # 保留你原有的course_params方法... # 新增批量参数的强校验规则,对应前端的参数结构 def batch_course_params params.permit( golf_courses: [ :remote_api_id, :name, :phone, holes_attributes: [ :hole, :par, tees_attributes: [:name, :yardage, :par] # 这里根据你实际的字段调整 ] ] ) end end
别忘了在routes.rb里加对应的批量提交路由:
namespace :admin do resources :golf_courses do collection do post :search post :batch_create # 新增这条批量创建的路由 end end end
第二步:重构视图,改成单表单+复选框结构
把原来的每个独立表单,改成一个大表单包裹所有可选球场,用复选框控制是否提交该球场的数据,已存在的球场直接标记为“已保存”:
<h1>Courses from API: <%= @courses.count %></h1> <%= form_with url: admin_golf_courses_batch_create_path, local: true do |form| %> <div class="grid grid-cols-1 gap-6"> <% @courses.each_with_index do |course_data, course_idx| %> <% existing_course = GolfCourse.find_by(remote_api_id: course_data[:remote_api_id]) %> <% if existing_course.present? %> <p class="mt-4 text-brand-green-vibrant font-semibold">已保存:<%= course_data[:name] %></p> <% else %> <div class="border p-4 rounded-lg hover:bg-gray-50 cursor-pointer"> <!-- 复选框:控制是否提交该球场 --> <%= check_box_tag "golf_courses[#{course_idx}][selected]", "1", true, id: "course_#{course_idx}" %> <%= label_tag "course_#{course_idx}", course_data[:name], class: "ml-2 font-medium text-lg" %> <!-- 隐藏字段:存储球场基础数据 --> <%= hidden_field_tag "golf_courses[#{course_idx}][remote_api_id]", course_data[:remote_api_id] %> <%= hidden_field_tag "golf_courses[#{course_idx}][name]", course_data[:name] %> <%= hidden_field_tag "golf_courses[#{course_idx}][phone]", course_data[:phone] %> <!-- 嵌套球洞数据:用Rails的_attributes命名规则,自动关联创建 --> <% course_data[:holes].each_with_index do |hole, hole_idx| %> <%= hidden_field_tag "golf_courses[#{course_idx}][holes_attributes][#{hole_idx}][hole]", hole[:hole] %> <%= hidden_field_tag "golf_courses[#{course_idx}][holes_attributes][#{hole_idx}][par]", hole[:par] %> <!-- 嵌套Tee台数据 --> <% hole[:tees].each_with_index do |tee, tee_idx| %> <%= hidden_field_tag "golf_courses[#{course_idx}][holes_attributes][#{hole_idx}][tees_attributes][#{tee_idx}][name]", tee[:name] %> <%= hidden_field_tag "golf_courses[#{course_idx}][holes_attributes][#{hole_idx}][tees_attributes][#{tee_idx}][par]", tee[:par] %> <!-- 这里补充你需要的其他Tee台字段 --> <% end %> <% end %> </div> <% end %> <% end %> </div> <!-- 批量提交按钮 --> <div class="mt-8"> <%= submit_tag "批量保存选中的球场", class: "bg-green-600 text-white py-2 px-6 rounded hover:bg-green-700" %> </div> <% end %>
视图小优化(可选)
加个简单的JS,让用户点击球场卡片就能切换复选框状态,提升操作体验:
document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('.border.p-4').forEach(card => { card.addEventListener('click', function(e) { if (!e.target.tagName.match(/INPUT|LABEL/)) { const checkbox = this.querySelector('input[type="checkbox"]'); checkbox.checked = !checkbox.checked; } }); }); });
为什么这是Rails惯用的方式?
- 符合RESTful思想:用
collection级别的batch_create动作,不破坏原有路由结构 - 嵌套属性原生支持:用
holes_attributes和tees_attributes的命名规则,Rails会自动处理嵌套关联的创建,不用手动写关联逻辑 - 前后端双重校验:前端隐藏已存在的球场,后端创建时再做一次重复判断,彻底避免数据重复
- 清晰的参数结构:索引化的参数数组,Rails能自动解析成可直接传给
new方法的属性哈希
内容来源于stack exchange
相关产品推荐
相关产品推荐

