【问题标题】:Import data from CSV into two tables in rails将数据从 CSV 导入到 rails 中的两个表中
【发布时间】:2019-01-28 00:40:15
【问题描述】:

我的 rails 应用程序中有一个 CSV 导入功能。我有父母桌和儿童桌。父母与孩子有很多联系。我能够从 CSV 中获取父级详细信息,但无法提取子级详细信息。

parents_controller.rb

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

父.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 %>

父.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

【问题讨论】:

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


    【解决方案1】:

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

    为了从上面演示您的示例,我使用 CSV 数据为 John、Jessica 和 Josh 编写了一个 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。

    【讨论】:

      【解决方案2】:

      您应该使用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 创建子代以避免重复。

      【讨论】:

      • 父记录被创建了两次。因为两个孩子属于同一个父母。我怎样才能解决这个问题?
      • 未加引号的字段不允许 \r 或 \n 这是我遇到的另一个错误
      • 如果你使用 find_or_create_by 传递相同的参数,它不应该创建两次父母。你有一个带有 parent_id 字段的子模型和表吗?如果是,您还应该能够独立于父级创建子级。随时分享您的更多代码。
      猜你喜欢
      • 2019-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多