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

使用ActiveAdmin在Rails应用程序中导入CSV数据,csv,ruby-on-rails-3.1,activeadmin,Csv,Ruby On Rails 3.1,Activeadmin,我想通过activeadmin面板上传CSV文件 在资源“product”的索引页上,我希望在“new product”按钮旁边有一个带有“import csv file”的按钮 我不知道从哪里开始。 在文档中有一些关于collection_操作的内容,但是对于下面的代码,我在顶部没有链接 ActiveAdmin.register Post do collection_action :import_csv, :method => :post do # Do some C

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

在资源“product”的索引页上,我希望在“new product”按钮旁边有一个带有“import csv file”的按钮

我不知道从哪里开始。 在文档中有一些关于collection_操作的内容,但是对于下面的代码,我在顶部没有链接

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数据的人?

添加
集合\u操作
不会自动添加链接到该操作的按钮。要在索引屏幕顶部添加按钮,需要将以下代码添加到
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

从托马斯·屈臣氏(Thomas Watsons)开始,继续回答这个问题,这个问题帮助我在了解其他问题之前了解了自己的方向

代码blow不仅允许对示例Posts模型进行CSV上传,还允许对随后的任何模型进行CSV上传。您只需将示例中的action_项和collection_操作复制到任何其他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
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
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
app/models/csv_db.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
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
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
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"
first_name,last_name,attribute1,attribute2
john,citizen,value1,value2

注意:引用并不总是需要的

@krhorst,我正试图使用您的代码,但不幸的是,它对大的导入很糟糕。它消耗了大量内存=(因此我决定使用自己的基于activerecord import gem的解决方案)

给你

特征

  • 编码处理
  • 支持使用ZIP文件导入
  • 两步导入(参见示例2)
  • CSV选项
  • 能够自动预编CSV标题
  • 批量导入(activerecord导入)
  • 能够自定义模板
  • 回调支持
  • 支持从zip文件导入
  • 基于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
    
    需要“csv”
    CsvDb类
    类true)do |行|
    目标\u model.create(行到\u散列)
    结束
    救援异常=>e
    Rails.logger.error e.消息
    Rails.logger.error e.backtrace.join(“\n”)
    结束
    结束
    结束
    结束
    

    虽然不是一个完整的答案,但我不希望我的更改会污染ben.m的答案,以防我做了一些非常错误的事情。

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

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

    app/models/csv_db.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
    
    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
    
    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
    

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


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

  • 导入具有不同顺序列的CSV数据
  • 防止Excel CSV中隐藏字符引起的错误
  • 重置数据库主键,以便应用程序可以在导入后继续添加记录
  • 注意:我取出了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
    
    需要“csv”
    CsvDb类
    
    为csv_db块初始化是否将模型_名称、目标_模型和新_对象替换为Post或Lead等相对名称?调用CsvDb时不需要简单地进行转换。转换_save(“Post”,params[:dump][:file])只需使用您喜欢的资源替换Post。如何覆盖任何现有条目?我得到一个
    SQLite3::ConstraintException:如果我试图在现有记录上导入CSV,则主键必须是唯一的
    错误。问题出在Id上。您可以按照if id==something.id something.update_attributes:blah=>“blah”end的行插入一个条件语句