将数据从CSV导入Rails的两个表中

时间:2018-08-21 19:09:31

标签: ruby-on-rails ruby-on-rails-5 import-csv

我的rails应用程序中具有导入CSV功能。我有父母桌和孩子桌。父母与孩子有很多联系。我可以从CSV获取父级详细信息,但无法提取子级详细信息。请帮忙。我该如何实现?

parents_controller.rb

def import
    @parent = Parent.import(params[:file])
    redirect_to main_admin_path, flash: { success: "Parents Imported" }
end

parent.rb

has_many :children, dependent: :destroy

def self.import(file)
    CSV.foreach(file.path, headers:true) do |row|
        Parent.create! row.to_hash
    end
end

routes.rb

resources :parents do
    collection { post :import }
 resources :children        
end

main_admin.html.erb

 Import Parents
  <%= form_tag import_parents_path, multipart:true do %>
   <%= file_field_tag :file %>
   <%= submit_tag "Import"%>
  <% end %>

parent.csv

parent_1_firstname,parent_1_lastname,address,childfirstname,childlastname,childdateofbirth
John,Wilson,68 Bell Road,Jessica,Wilson,2002-11-11 
John,Wilson,68 Bell Road,Josh,Wilson,2006-10-01

2 个答案:

答案 0 :(得分:0)

您应该使用find_or_create_by方法来创建您的父母。

仅在那之后,您应该创建子代。它是这样的:

# parent.rb
def self.import(file)
  CSV.foreach(file.path, headers:true) do |row|
    parent = Parent.find_or_create_by(
      name: row["parent_1_firstname"] + row["parent_1_lastname"],
      address: row["address"],
      # ...
    )

    parent.children.find_or_create_by(
      name: row["childfirstname"] + row["childlastname"],
      birthday: row["childdateofbirth"],
      # ...
    )
  end
end

请注意,您也可以使用find_or_create_by创建子代,以避免重复。

答案 1 :(得分:0)

为了不必通过find_or_create_by逻辑对各种嵌套循环进行硬编码,我刚刚发布了一个名为 DutyFree 的gem,它使这样的导入和导出相当轻松。它可以智能地分析模型上的has_many和belongs_to关联,并基于这些关系确定如何正确地跨多个目标表保存每个导入的行。根据数据是否存在执行创建或更新。

为了从上面演示您的示例,我为John,Jessica和Josh用CSV数据编写了一个RSpec测试: https://github.com/lorint/duty_free/blob/master/spec/models/parent_simple_spec.rb

在这里可以看到更多使用相同父级和子级模型的示例: https://github.com/lorint/duty_free/blob/master/spec/models/parent_complex_spec.rb

关于这个gem的一件好事是,在配置了列定义以进行导入之后,由于所有工作都来自同一模板,因此您可以免费获得导出。对于使用CSV的示例,以下是模板,该模板允许CSV中的列名与数据库列完美对齐:

IMPORT_TEMPLATE = {
  uniques: [:firstname, :children_firstname],
  required: [],
  all: [:firstname, :lastname, :address,
    { children: [:firstname, :lastname, :dateofbirth] }],
  as: {
        'parent_1_firstname' => 'Firstname',
        'parent_1_lastname' => 'Lastname',
        'address' => 'Address',
        'childfirstname' => 'Children Firstname',
        'childlastname' => 'Children Lastname',
        'childdateofbirth' => 'Children Dateofbirth'
      }
}.freeze

将其添加到“父模型”中,您就可以动弹了,可以调用#df_import和#df_export。