使用ActiveAdmin在Rails应用程序中导入CSV数据

时间:2011-10-13 13:32:44

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

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

在资源“product”的索引页面上我想要一个带有“import csv file”的“new product”按钮旁边的按钮。

我不知道从哪里开始。 在文档中是关于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数据?

8 个答案:

答案 0 :(得分:42)

继续托马斯·沃特森的良好开端,帮助我在确定其余部分之前找到了答案。

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

应用/管理/ 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

应用/模型/ 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以供参考)< / em>的

应用/视图/管理/ 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'

应用/公共/ 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"

注意:并非总是需要报价

答案 1 :(得分:13)

添加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

答案 2 :(得分:6)

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

答案 3 :(得分:2)

为了将来的参考,我构建了一个gem,可以让你轻松地将csv import添加到活动的管理资源

See this link

答案 4 :(得分:2)

根据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 :(得分:0)

扩展ben.m的反应,我发现它非常有用。

我遇到了CSV导入逻辑的问题(属性没有排列,列迭代器没有按要求运行)并实现了一个更改,而是使用了每行循环和model.create方法。这允许您导入带有与属性匹配的标题行的.csv。

应用/模型/ 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 :(得分:0)

对于在正常流程上花费时间的大型Excel,我创建了一个使用活动作业处理 Excel工作表并使用操作电缆(websockets)显示结果的gem

enter image description here

答案 7 :(得分:0)

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

  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