it-roy-ru.com

Как преобразовать массив моделей ActiveRecord в CSV?

Я получил массив моделей ActiveRecord, которые я хочу преобразовать в CSV. Я пытался исследовать драгоценные камни, такие как FasterCSV, но, похоже, они работают только со строками и массивами, а не с моделями ActiveRecord.

Короче хочу конвертировать:

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

Есть ли простой способ Rails сделать это?

36
Henley Chiu

Следующее запишет атрибуты всех пользователей в файл:

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
90
rudolph9

Ответ @ rudolph9 действительно потрясающий. Я просто хочу оставить записку для людей, которым нужно периодически выполнять эту задачу: было бы неплохо сделать это заданием с граблями!

библиотека/Задачи/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
11
Brian

Если вам нужно что-то быстрое и грязное, не столько для производства, сколько просто для сбора данных для нетехнического пользователя, вы можете вставить это в консоль:

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
Mario Olivio Flores

Это может быть от первоначального вопроса, но решить проблему. Если вы планируете сделать так, чтобы все или некоторые из ваших моделей 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
theterminalguy

Еще один аналогичный ответ, но вот что я обычно делаю.

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
1
Yuki Inoue

с 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
Steven Barragán

Для этого также можно использовать движок 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 - он поставляется с sqlite, выпущенным в 2013 году. Эта версия еще не знала separator и once. Поэтому вам может потребоваться загрузить последний бинарный файл с веб-сайта: https://sqlite.org/download.html установить его локально и использовать полный путь к локальной установке:

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