54

我得到了一组我希望转换为 CSV 的 ActiveRecord 模型。我尝试研究像 FasterCSV 这样的 gem,但它们似乎只适用于字符串和数组,而不是 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 方法可以做到这一点?

4

8 回答 8

114

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

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
于 2013-10-05T17:51:52.763 回答
11

@rudolph9 的回答真的很棒。我只是想给需要定期执行此任务的人留个便条:将其作为 rake 任务将是一个好主意!

lib/tasks/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
于 2014-05-01T08:32:29.537 回答
5

这可能与原始问题无关,但可以解决问题。如果您计划让所有或部分 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 关系,不适用于数组

于 2018-04-10T08:39:04.707 回答
4

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

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 周围的装饰器,并且更准确地确保您的字段/属性的顺序。

于 2017-01-30T12:16:42.137 回答
2

使用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)
于 2015-12-11T23:35:12.767 回答
2

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

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方法将可用于模型及其关系。例如

User.where(role: :customer).to_csv
# => gets the csv string of user whose role is :customer
于 2018-12-04T05:53:19.707 回答
0

我遇到了同样的问题并结合了其中的几个答案,因此我可以在模型或关系上调用 to_csv,然后输入文件名并创建一个 csv 文件。

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

  def self.to_csv
    require 'csv'

    p "What is the name of your file? (don't forget .csv at the end)"

    file_name = gets.chomp

    CSV.open("#{file_name}", "wb") do |csv|
      csv << column_names
      all.find_each do |model|
        csv << model.attributes.values_at(*column_names)
      end
    end
  end
end

现在,您可以从控制台调用.to_csv任何模型或任何数据库查询或活动记录关系。

于 2020-03-24T22:04:05.407 回答
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

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

~/.local/bin/sqlite3 -init lib/tasks/export-submissions.sql db/development.sqlite3 .exit
于 2018-07-31T08:54:52.413 回答