Подтвердить что ты не робот

Как преобразовать массив моделей 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 для этого?

4b9b3361

Ответ 1

Далее будут записываться атрибуты всех пользователей в файл:

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

Аналогичным образом вы можете создать строку CSV:

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

Ответ 2

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

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

Ответ 3

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

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

Тогда do: User.select(:id,:name).all.to_csv

Если бы вы собирались на производство, я бы, вероятно, превратил это в декоратора вокруг ActiveRecord:: Relation и, более точно, чтобы обеспечить порядок ваших полей/атрибутов.

Ответ 4

с julia_builder вы можете легко настроить экспорт csv.

class UserCsv < Julia::Builder
  # specify column 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)

Ответ 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, а не для массивов

Ответ 6

Для этого также можно использовать движок 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

Ответ 7

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

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