使用Roo gem导入数据时如何实现记录存在即更新
核心错误说明
你的代码存在三处致命问题,导致去重更新逻辑完全无法运行:
if product?属于无效判断:你刚通过Product.new实例化了一个全新的内存对象,这个判断永远返回真值,根本没有执行数据库查询来确认对应记录是否已存在。update_attributes调用时未传入待更新的属性参数,会直接抛出参数缺失错误。rescue代码块位置错误,语法层级不匹配,无法正确捕获单条数据处理时抛出的异常。
正确实现方案
首先你需要先确定业务上判定「产品已存在」的唯一标识字段,通常是SKU、商品条码、货号这类全局唯一的字段,不建议用商品名name作为判断依据,容易出现重名误判。以下示例用sku作为唯一判断字段,你可以替换成自己业务中的实际唯一键。
修正后的完整rake任务代码:
namespace :import do desc "Import data from spreadsheet" task data: :environment do data = Roo::Spreadsheet.open('lib/t3.xlsx') headers = data.row(1) data.each_with_index do |row, idx| next if idx == 0 # 跳过表头行 product_data = Hash[[headers, row].transpose] # 先根据唯一键查询数据库中是否存在对应记录 product = Product.find_by(sku: product_data['sku']) if product puts "Updating Product: #{product_data['name']}" product.update!(product_data) else puts "Creating Product: #{product_data['name']}" Product.create!(product_data) end rescue ActiveRecord::RecordInvalid => e puts "Row #{idx + 1} process failed: #{e.record.errors.full_messages.join(', ')}" end end end
注意事项
- Rails 4及以上版本中,
update_attributes已经正式别名成update,建议直接使用update!/create!的带感叹号版本,遇到校验失败会直接抛出异常,配合rescue可以快速定位错误行的问题。 - 如果你的重复判断规则是多字段组合(比如同分类下商品名不重复),可以给
find_by传入组合查询条件,例如:Product.find_by(category_id: product_data['category_id'], name: product_data['name']) - 建议给用于判断重复的字段(或字段组合)添加数据库唯一索引,从底层避免重复数据写入。
内容的提问来源于stack exchange,提问作者Gibson
相关产品推荐
相关产品推荐

