ActiveRecord::AssociationTypeMismatch Rails CSV 导入
ActiveRecord::AssociationTypeMismatch Rails CSV Import
我正在使用 gem roo
导入 CSV 数据。它运行顺利,直到有关联的地方,我希望 roo 可以将字符串转换为关联中相应的整数值。就我而言,我有一个属于 State
.
的 Staff
模型
class State < ApplicationRecord
has_many :staffs
end
class Staff < ApplicationRecord
belongs_to :state
end
这意味着我在 staffs
table 中有 state_id
列。但是,在我的 CSV 中,最终用户拥有州的名称,对应于 states
table 中的州名称。当我尝试导入 CSV 时,出现错误:
ActiveRecord::AssociationTypeMismatch in StaffsImportsController#create
State(#134576500) expected, got "Texas" which is an instance of String(#20512180)
突出显示的来源是:
staff.attributes = row.to_hash
gem roo
是否可以将 csv 文件中的 'Texas' 翻译成 id 2,而不是最终用户在上传数据之前做大量的翻译工作?
这里是staffs_imports.rb
class StaffsImport
include ActiveModel::Model
require 'roo'
attr_accessor :file
def initialize(attributes={})
attributes.each { |name, value| send("#{name}=", value) }
end
def persisted?
false
end
def open_spreadsheet
case File.extname(file.original_filename)
when ".csv" then Csv.new(file.path, nil, :ignore)
when ".xls" then Roo::Excel.new(file.path, nil, :ignore)
when ".xlsx" then Roo::Excelx.new(file.path)
else raise "Unknown file type: #{file.original_filename}"
end
end
def load_imported_staffs
spreadsheet = open_spreadsheet
header = spreadsheet.row(1)
(2..spreadsheet.last_row).map do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
staff = Staff.find_by_national_id(row["national_id"]) || Staff.new
staff.attributes = row.to_hash
staff
end
end
def imported_staffs
@imported_staffs ||= load_imported_staffs
end
def save
if imported_staffs.map(&:valid?).all?
imported_staffs.each(&:save!)
true
else
imported_staffs.each_with_index do |staff, index|
staff.errors.full_messages.each do |msg|
errors.add :base, "Row #{index + 6}: #{msg}"
end
end
false
end
end
end
最后 staff_imports_controller.rb
:
class StaffsImportsController < ApplicationController
def new
@staffs_import = StaffsImport.new
end
def create
@staffs_import = StaffsImport.new(params[:staffs_import])
if @staffs_import.save
flash[:success] = "You have successfully uploaded your staff!"
redirect_to staffs_path
else
render :new
end
end
end
任何 help/clues 将不胜感激。
由于这里提供了一个非常详细的问题和很好的答案,我设法找到了解决方案
我正在使用 gem roo
导入 CSV 数据。它运行顺利,直到有关联的地方,我希望 roo 可以将字符串转换为关联中相应的整数值。就我而言,我有一个属于 State
.
Staff
模型
class State < ApplicationRecord
has_many :staffs
end
class Staff < ApplicationRecord
belongs_to :state
end
这意味着我在 staffs
table 中有 state_id
列。但是,在我的 CSV 中,最终用户拥有州的名称,对应于 states
table 中的州名称。当我尝试导入 CSV 时,出现错误:
ActiveRecord::AssociationTypeMismatch in StaffsImportsController#create
State(#134576500) expected, got "Texas" which is an instance of String(#20512180)
突出显示的来源是:
staff.attributes = row.to_hash
gem roo
是否可以将 csv 文件中的 'Texas' 翻译成 id 2,而不是最终用户在上传数据之前做大量的翻译工作?
这里是staffs_imports.rb
class StaffsImport
include ActiveModel::Model
require 'roo'
attr_accessor :file
def initialize(attributes={})
attributes.each { |name, value| send("#{name}=", value) }
end
def persisted?
false
end
def open_spreadsheet
case File.extname(file.original_filename)
when ".csv" then Csv.new(file.path, nil, :ignore)
when ".xls" then Roo::Excel.new(file.path, nil, :ignore)
when ".xlsx" then Roo::Excelx.new(file.path)
else raise "Unknown file type: #{file.original_filename}"
end
end
def load_imported_staffs
spreadsheet = open_spreadsheet
header = spreadsheet.row(1)
(2..spreadsheet.last_row).map do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
staff = Staff.find_by_national_id(row["national_id"]) || Staff.new
staff.attributes = row.to_hash
staff
end
end
def imported_staffs
@imported_staffs ||= load_imported_staffs
end
def save
if imported_staffs.map(&:valid?).all?
imported_staffs.each(&:save!)
true
else
imported_staffs.each_with_index do |staff, index|
staff.errors.full_messages.each do |msg|
errors.add :base, "Row #{index + 6}: #{msg}"
end
end
false
end
end
end
最后 staff_imports_controller.rb
:
class StaffsImportsController < ApplicationController
def new
@staffs_import = StaffsImport.new
end
def create
@staffs_import = StaffsImport.new(params[:staffs_import])
if @staffs_import.save
flash[:success] = "You have successfully uploaded your staff!"
redirect_to staffs_path
else
render :new
end
end
end
任何 help/clues 将不胜感激。
由于这里提供了一个非常详细的问题和很好的答案,我设法找到了解决方案