27

我想通过 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数据吗?

4

7 回答 7

45

继续从Thomas Watsons的良好开端到答案,它帮助我在弄清楚其余部分之前了解我的方位。

代码攻击不仅允许示例 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 以供参考)

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'

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

注意:并不总是需要报价

于 2012-03-25T09:13:07.640 回答
14

Adding a collection_action does not automatically add a button linking to that action. To add a button at the top of the index screen you need to add the following code to your ActiveAdmin.register block:

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

But before calling the collection action you posted in your question, you first need the user to specify which file to upload. I would personally do this on another screen (i.e. creating two collection actions - one being a :get action, the other being your :post action). So the complete AA controller would look something like this:

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
于 2011-10-13T14:34:35.540 回答
9

@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. ……
于 2013-03-11T15:53:51.720 回答
3

根据上面 ben.m 的出色回答,我将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 的答案,以防我做错了什么。

于 2013-07-09T23:14:36.867 回答
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 文件可能如下所示(用于匹配模型属性):

导入示例.csv

first_name,last_name,attribute1,attribute2
john,citizen,value1,value2
于 2014-08-28T02:45:44.513 回答
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
于 2019-09-14T04:18:25.690 回答
0

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

https://github.com/shivgarg5676/active_admin_excel_upload

于 2017-08-27T06:40:04.167 回答