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