How to convert array of ActiveRecord models to CSV?

后端 未结 8 1466
情话喂你
情话喂你 2020-12-23 13:34

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 Activ

相关标签:
8条回答
  • 2020-12-23 14:03

    @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!

    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
    
    0 讨论(0)
  • 2020-12-23 14:08

    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

    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.

    0 讨论(0)
  • 2020-12-23 14:08

    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 ApplicationRecord class, the base class of all the models (usually).

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

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

    User.where(role: :customer).to_csv
    # => gets the csv string of user whose role is :customer
    
    0 讨论(0)
  • 2020-12-23 14:09

    One can also utilize the sql engine for this. E.g. for 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 separator and once yet. So you might need to download the latest binary from the web-site: https://sqlite.org/download.html install it locally, and use the full path to the local installation:

    ~/.local/bin/sqlite3 -init lib/tasks/export-submissions.sql db/development.sqlite3 .exit
    
    0 讨论(0)
  • 2020-12-23 14:15

    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_string = CSV.generate do |csv|
      csv << User.attribute_names
      User.find_each do |user|
        csv << user.attributes.values
      end
    end
    
    0 讨论(0)
  • 2020-12-23 14:15

    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.

    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_csv on any model or any db query or activerecord relation.

    0 讨论(0)
提交回复
热议问题