Как преобразовать массив моделей 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 сделать это?
9 ответов
Следующее запишет атрибуты всех пользователей в файл:
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
Ответ @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
Это может быть от первоначального вопроса, но решить проблему. Если вы планируете сделать так, чтобы все или некоторые из ваших моделей 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, а не для массивов
Если вам нужно что-то быстрое и грязное, не столько для производства, сколько просто для сбора данных для нетехнического пользователя, вы можете вставить это в консоль:
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 и, более точно, обеспечил бы порядок ваших полей / атрибутов.
С помощью 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)
Еще один аналогичный ответ, но вот что я обычно делаю.
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
У меня была такая же проблема, и я объединил пару этих ответов, чтобы я мог вызвать 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
Теперь с консоли можно позвонить .to_csv
для любой модели, любого запроса к базе данных или отношения activerecord.
Я сделал метод в ApplicationRecord, который позволяет вам сделать csv-укус из любой модели, он также использует только заголовки из выбора.
class ApplicationRecord < ActiveRecord::Base
class << self
def to_csv_string
CSV.generate do |csv|
csv << self.first.as_json.keys
self.find_each do |selfie|
csv << selfie.attributes.values
end
end
end
end
end
# User.select(:id, :name, :email).to_csv_string
# => "id,fname,lname\n1,Chet,Corey\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