如何将ActiveRecord模型数组转换为CSV?

问题描述 投票:40回答:7

我得到了一系列我希望转换为CSV的ActiveRecord模型。我尝试过研究像FasterCSV这样的宝石,但它们似乎只使用字符串和数组,而不是ActiveRecord模型。

简而言之,我想转换:

user1 = User.first
user2 = User.last
a = [user1, user2]

至:

   id,username,bio,email
    1,user1,user 1 bio,user1 email
    1,user2,user 2 bio,user2 email

有一个简单的Rails方法来做到这一点?

ruby-on-rails ruby csv
7个回答
97
投票

以下内容将所有用户的属性写入文件:

CSV.open("path/to/file.csv", "wb") do |csv|
  csv << User.attribute_names
  User.find_each do |user|
    csv << user.attributes.values
  end
end

同样,您可以创建CSV字符串:

csv_string = CSV.generate do |csv|
  csv << User.attribute_names
  User.find_each do |user|
    csv << user.attributes.values
  end
end

11
投票

@ rudolph9的答案非常棒。我只想给那些需要定期完成这项任务的人留一张纸条:将它作为一个rake任务是一个好主意!

LIB /任务/ users_to_csv.rake

# usage:
# rake csv:users:all => export all users to ./user.csv
# rake csv:users:range start=1757 offset=1957 => export users whose id are between 1757 and 1957
# rake csv:users:last number=3   => export last 3 users
require 'csv' # according to your settings, you may or may not need this line

namespace :csv do
  namespace :users do
    desc "export all users to a csv file"
    task :all => :environment do
      export_to_csv User.all
    end

    desc "export users whose id are within a range to a csv file"
    task :range => :environment do |task, args|
      export_to_csv User.where("id >= ? and id < ?", ENV['start'], ENV['offset'])
    end

    desc "export last #number users to a csv file"
    task :last => :environment do |task, arg|
      export_to_csv User.last(ENV['number'].to_i)
    end

    def export_to_csv(users)
      CSV.open("./user.csv", "wb") do |csv|
        csv << User.attribute_names
        users.each do |user|
          csv << user.attributes.values
        end
      end
    end
  end
end

2
投票

如果您需要快速而肮脏的东西,而不仅仅是为非技术用户抓取一些数据,那么您可以将其粘贴到控制台中:

require 'csv'
class ActiveRecord::Relation
  def to_csv
    ::CSV.generate do |csv|
      csv << self.model.attribute_names
      self.each do |record|
        csv << record.attributes.values
      end
    end
  end
end

然后做:User.select(:id,:name).all.to_csv

如果你要去制作,我可能会把它变成一个围绕ActiveRecord :: Relation的装饰器,更准确地说是确保你的字段/属性的顺序。


2
投票

这可能不是原来的问题,但解决了问题。如果您计划将所有或部分Active Record模型转换为csv,则可以使用ActiveRecord。一个例子如下所示

module Csvable
  extend ActiveSupport::Concern 

  class_methods do
    def to_csv(*attributes)
      CSV.generate(headers: true) do |csv| 
        csv << attributes 

        all.each do |record| 
          csv << attributes.map { |attr| record.send(attr) }
        end 
      end
    end
  end
end

提供的属性将用作CSV的标头,并且预期此属性对应于包含的类中的方法名称。然后,您可以将它包含在您选择的任何ActiveRecord类中,在本例中为User类

class User 
  include Csvable 

end

用法

User.where(id: [1, 2, 4]).to_csv(:id, :name, :age)

注意:这仅适用于ActiveRecord关系,不适用于数组


1
投票

使用julia_builder,您可以非常轻松地配置csv导出。

class UserCsv < Julia::Builder
  # specify column's header and value
  column 'Birthday', :dob
  # header equals 'Birthday' and the value will be on `user.dbo`

  # when header and value are the same, no need to duplicate it.
  column :name
  # header equals 'name', value will be `user.name`

  # when you need to do some extra work on the value you can pass a proc.
  column 'Full name', -> { "#{ name.capitalize } #{ last_name.capitalize }" }

  # or you can pass a block
  column 'Type' do |user|
    user.class.name
  end
end

然后

users = User.all
UserCsv.build(users)

1
投票

还有一个类似的答案,但这就是我通常做的事情。

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  def self.to_csv
    CSV.generate do |csv|
      csv << column_names
      all.find_each do |model|
        csv << model.attributes.values_at(*column_names)
      end
    end
  end
end

我通常将这些代码放在ApplicationRecord类中,而不是黑客攻击现有的模块,这是所有模型的基类(通常)。

如果需要进一步详细说明,我会在to_csv方法中添加一个命名参数,并在此类中尽可能多地处理这些功能。

这样,to_csv方法将可用于Model及其Relation。例如。

User.where(role: :customer).to_csv
# => gets the csv string of user whose role is :customer

0
投票

也可以使用sql引擎。例如。对于sqlite3:

cat << EOF > lib/tasks/export-submissions.sql
.mode      csv
.separator ',' "\n"
.header    on


.once "submissions.csv"

select
  *
from submissions
;
EOF

sqlite3 -init lib/tasks/export-submissions.sql db/development.sqlite3 .exit

如果你在CentOS 7上 - 它随附2013年发布的sqlite。那个版本还不知道separatoronce。因此,您可能需要从网站下载最新的二进制文件:https://sqlite.org/download.html在本地安装它,并使用本地安装的完整路径:

~/.local/bin/sqlite3 -init lib/tasks/export-submissions.sql db/development.sqlite3 .exit
© www.soinside.com 2019 - 2024. All rights reserved.