【问题标题】:Import CSV Data in a Rails App with ActiveAdmin使用 ActiveAdmin 在 Rails 应用程序中导入 CSV 数据
【发布时间】:2011-10-13 13:32:44
【问题描述】:

我想通过 activeadmin 面板上传 CSV 文件。

在资源“产品”的索引页面上,我想要一个带有“导入 csv 文件”的“新产品”按钮旁边的按钮。

我不知道从哪里开始。 在文档中是关于collection_action的东西,但是下面的代码我没有顶部的链接。

ActiveAdmin.register Post do
    collection_action :import_csv, :method => :post do
      # Do some CSV importing work here...
      redirect_to :action => :index, :notice => "CSV imported successfully!"
    end
  end

这里有人使用activeadmin并且可以导入csv数据吗?

【问题讨论】:

    标签: csv ruby-on-rails-3.1 activeadmin


    【解决方案1】:

    @krhorst,我试图使用你的代码,但不幸的是它在大量导入时很糟糕。它吃了这么多内存=(所以我决定使用基于activerecord-import gem的自己的解决方案

    这里是https://github.com/Fivell/active_admin_import

    特点

    1. 编码处理
    2. 支持ZIP文件导入
    3. 两步导入(参见示例 2)
    4. CSV 选项
    5. 能够自动添加 CSV 标头
    6. 批量导入 (activerecord-import)
    7. 能够自定义模板
    8. 回调支持
    9. 支持从 zip 文件导入
    10. ....

    【讨论】:

      【解决方案2】:

      Thomas Watsons 开始,这是一个很好的开始,它帮助我在弄清楚其余部分之前了解了方向。

      代码攻击不仅允许上传示例 Posts 模型的 CSV 文件,还允许上传之后的任何后续模型。您需要做的就是将示例中的 action_item 和 collection_actions 复制到任何其他 ActiveAdmin.register 块中,功能将是相同的。希望这会有所帮助。

      app/admin/posts.rb

      ActiveAdmin.register Post do
        action_item :only => :index do
          link_to 'Upload CSV', :action => 'upload_csv'
        end
      
        collection_action :upload_csv do
          render "admin/csv/upload_csv"
        end
      
        collection_action :import_csv, :method => :post do
          CsvDb.convert_save("post", params[:dump][:file])
          redirect_to :action => :index, :notice => "CSV imported successfully!"
        end
      
      end
      

      app/models/csv_db.rb

      require 'csv'
      class CsvDb
        class << self
          def convert_save(model_name, csv_data)
            csv_file = csv_data.read
            CSV.parse(csv_file) do |row|
              target_model = model_name.classify.constantize
              new_object = target_model.new
              column_iterator = -1
              target_model.column_names.each do |key|
                column_iterator += 1
                unless key == "ID"
                  value = row[column_iterator]
                  new_object.send "#{key}=", value
                end
              end
              new_object.save
            end
          end
        end
      end
      

      注意:此示例检查第一列是否为 ID 列,然后跳过该列,因为 rails 将为新对象分配 ID(请参阅下面的示例 CSV 以供参考)

      app/views/admin/csv/upload_csv.html.haml

      = form_for :dump, :url=>{:action=>"import_csv"}, :html => { :multipart => true } do |f|
        %table
          %tr
            %td
              %label{:for => "dump_file"}
                Select a CSV File :
            %td
              = f.file_field :file
          %tr
            %td
              = submit_tag 'Submit'
      

      app/public/example.csv

      "1","TITLE EXAMPLE","MESSAGE EXAMPLE","POSTED AT DATETIME"
      "2","TITLE EXAMPLE","MESSAGE EXAMPLE","POSTED AT DATETIME"
      "3","TITLE EXAMPLE","MESSAGE EXAMPLE","POSTED AT DATETIME"
      "4","TITLE EXAMPLE","MESSAGE EXAMPLE","POSTED AT DATETIME"
      "5","TITLE EXAMPLE","MESSAGE EXAMPLE","POSTED AT DATETIME"
      

      注意:并非总是需要引用

      【讨论】:

      • 对于 csv_db 块,您是否将 model_name、target_model 和 new_object 替换为 Post 或 Lead 等相对名称?
      • 您不需要简单地在调用 CsvDb.convert_save("post", params[:dump][:file]) 时将帖子替换为您想要的资源
      • 如何覆盖任何现有条目?如果我尝试通过现有记录导入 CSV,则会收到 SQLite3::ConstraintException: PRIMARY KEY must be unique 错误。
      • 它的 ID 是问题所在。您可以按照 if id == something.id something.update_attributes :blah => "blah" end 的方式输入条件语句
      【解决方案3】:

      添加collection_action 不会自动添加链接到该操作的按钮。要在索引屏幕顶部添加一个按钮,您需要将以下代码添加到您的 ActiveAdmin.register 块中:

      action_item :only => :index do
        link_to 'Upload CSV', :action => 'upload_csv'
      end
      

      但在调用您在问题中发布的收集操作之前,您首先需要用户指定要上传的文件。我会亲自在另一个屏幕上执行此操作(即创建 两个 集合操作 - 一个是 :get 操作,另一个是您的 :post 操作)。所以完整的 AA 控制器看起来像这样:

      ActiveAdmin.register Post do
        action_item :only => :index do
          link_to 'Upload posts', :action => 'upload_csv'
        end
      
        collection_action :upload_csv do
          # The method defaults to :get
          # By default Active Admin will look for a view file with the same
          # name as the action, so you need to create your view at
          # app/views/admin/posts/upload_csv.html.haml (or .erb if that's your weapon)
        end
      
        collection_action :import_csv, :method => :post do
          # Do some CSV importing work here...
          redirect_to :action => :index, :notice => "CSV imported successfully!"
        end
      end
      

      【讨论】:

        【解决方案4】:

        根据上面 ben.m 的 excellent answer,我将建议的 csv_db.rb 部分替换为:

        require 'csv'
        class CsvDb
          class << self
            def convert_save(model_name, csv_data)
              begin
                target_model = model_name.classify.constantize
                CSV.foreach(csv_data.path, :headers => true) do |row|
                  target_model.create(row.to_hash)
                end
              rescue Exception => e
                Rails.logger.error e.message
                Rails.logger.error e.backtrace.join("\n")
              end
            end
          end
        end
        

        虽然不是一个完整的答案,但我不希望我的更改污染 ben.m 的答案,以防我做错了什么。

        【讨论】:

          【解决方案5】:

          扩展 ben.m 的回复,我发现这非常有用。

          我遇到了 CSV 导入逻辑的问题(属性未对齐且列迭代器未按要求运行)并实施了一项更改,该更改改为使用每行循环和 model.create 方法。这允许您导入带有与属性匹配的标题行的 .csv。

          app/models/csv_db.rb

          require 'csv'
          class CsvDb
            class << self
              def convert_save(model_name, csv_data)
                csv_file = csv_data.read
                lines = CSV.parse(csv_file)
                header = lines.shift
                lines.each do |line|
                  attributes = Hash[header.zip line]
                  target_model = model_name.classify.constantize
                  target_model.create(attributes)
                end
              end
            end
          end
          

          因此您导入的 CSV 文件可能如下所示(用于匹配模型属性):

          importExample.csv

          first_name,last_name,attribute1,attribute2
          john,citizen,value1,value2
          

          【讨论】:

            【解决方案6】:

            对于在正常过程中需要时间的大型 excel,我创建了一个 gem,它使用活动作业处理 Excel 表,并使用操作电缆(websockets)显示结果

            https://github.com/shivgarg5676/active_admin_excel_upload

            【讨论】:

              【解决方案7】:

              上面的一些解决方案效果很好。我在实践中遇到了我在下面解决的挑战。解决的问题是:

              1. 导入包含不同顺序列的 CSV 数据
              2. 防止 Excel CSV 中的隐藏字符导致的错误
              3. 重置数据库primary_key,以便应用程序可以在导入后继续添加记录

              注意:我取出了 ID 过滤器,因此我可以更改我正在处理的 ID,但大多数用例可能希望保留它。

              require 'csv'
              class CsvDb
                class << self
                  def convert_save(model_name, csv_data)
                    csv_file = csv_data.read
                    csv_file.to_s.force_encoding("UTF-8")
                    csv_file.sub!("\xEF\xBB\xBF", '')
                    target_model = model_name.classify.constantize
                    headers = csv_file.split("\n")[0].split(",")
                    CSV.parse(csv_file, headers: true) do |row|
                      new_object = target_model.new
                      column_iterator = -1
                      headers.each do |key|
                        column_iterator += 1
                        value = row[column_iterator]
                        new_object.send "#{key.chomp}=", value
                      end
                      new_object.save
                    end
                    ActiveRecord::Base.connection.reset_pk_sequence!(model_name.pluralize)
                  end
                end
              end
              

              【讨论】:

                猜你喜欢
                • 2014-03-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多