Ruby-on-rails 如何将 ActiveRecord 模型数组转换为 CSV?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19200734/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:49:38  来源:igfitidea点击:

How to convert array of ActiveRecord models to CSV?

ruby-on-railsrubycsv

提问by Henley Chiu

I got an array of ActiveRecord models that I wish to convert to a CSV. I tried researching gems like FasterCSV, but they just seem to work with strings and arrays, not ActiveRecord models.

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

In short, I want to convert:

总之,我想转换:

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

TO:

到:

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

Is there an easy Rails way to do this?

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

回答by rudolph9

The following will write the attributes of all users to a file:

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

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

Similarly you could create a CSV string:

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

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

回答by Brian

@rudolph9's answer is really awesome. I just want to leave a note for people who need to do this task periodically: making it as a rake task would be a good idea!

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

lib/tasks/users_to_csv.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

回答by Mario Olivio Flores

If you need something quick and dirty, not so much for production as just grabbing some data for a non-technical user, you could paste this in console:

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

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

Then do: User.select(:id,:name).all.to_csv

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

If you were going to production, I'd probably turn this into a decorator around ActiveRecord::Relation and more precisely ensuring that the order of your fields/attributes.

如果您要进行生产,我可能会将其变成 ActiveRecord::Relation 周围的装饰器,并更准确地确保您的字段/属性的顺序。

回答by theterminalguy

This might be off the original question but solve the problem. If you plan to make all or some of your Active Record models be able to convert to csv, you can use ActiveRecord concern. An example is shown below

这可能与原始问题无关,但可以解决问题。如果您打算让所有或部分 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

The attribute provided will be used as the header for the CSV and it is expected that this attribute corresponds to methods name in the included class. Then you can include it in any ActiveRecord class of your choice, in this case, the User class

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

class User 
  include Csvable 

end

Usage

用法

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

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

Note: This only works for ActiveRecord relation and not for arrays

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

回答by Steven Barragán

with julia_builderyou can configure a csv export pretty easily.

使用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

and then

进而

users = User.all
UserCsv.build(users)

回答by Yuki Inoue

Yet another similar answer, but here's what I usually do.

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

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

Instead of hacking existing module, I'd usually put this code in the ApplicationRecordclass, the base class of all the models (usually).

我通常会将此代码放在ApplicationRecord所有模型的基类(通常)的类中,而不是破解现有模块。

If any further elaboration is needed, I'd add a named parameter to the to_csvmethod, and handle those features as much as possible in this class.

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

This way, the to_csvmethod will be available to both Model and its Relation. E.g.

这样,该to_csv方法将可用于模型及其关系。例如

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

回答by tomb

I had this same problem and combined a couple of these answers so I could call to_csv on a model or relation, then input a file name and create a csv file.

我遇到了同样的问题,并结合了几个这些答案,所以我可以在模型或关系上调用 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

Now from console you can call .to_csvon any model or any db query or activerecord relation.

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

回答by Adobe

One can also utilize the sql engine for this. E.g. for sqlite3:

也可以为此使用 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

If you are on CentOS 7 -- it ships with sqlite released in 2013. That version did not know separatorand onceyet. So you might need to download the latest binary from the web-site: https://sqlite.org/download.htmlinstall it locally, and use the full path to the local installation:

如果你是在CentOS 7 -它附带源码在2013年发布的版本不知道separatoronce呢。所以你可能需要从网站下载最新的二进制文件:https: //sqlite.org/download.html在本地安装它,并使用本地安装的完整路径:

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