J'ai un tableau de modèles ActiveRecord que je souhaite convertir en CSV. J'ai essayé de rechercher des pierres précieuses comme FasterCSV, mais elles semblent fonctionner uniquement avec des chaînes et des tableaux, pas avec des modèles ActiveRecord.
En bref, je veux convertir:
user1 = User.first
user2 = User.last
a = [user1, user2]
À:
id,username,bio,email
1,user1,user 1 bio,user1 email
1,user2,user 2 bio,user2 email
Existe-t-il un moyen simple de faire cela?
Les éléments suivants écrivent les attributs de tous les utilisateurs dans un fichier:
CSV.open("path/to/file.csv", "wb") do |csv|
csv << User.attribute_names
User.find_each do |user|
csv << user.attributes.values
end
end
De même, vous pouvez créer une chaîne CSV:
csv_string = CSV.generate do |csv|
csv << User.attribute_names
User.find_each do |user|
csv << user.attributes.values
end
end
La réponse de @ rudolph9 est vraiment géniale. Je veux juste laisser un mot aux personnes qui doivent effectuer cette tâche périodiquement: le faire comme une tâche de commission serait une bonne idée!
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
Si vous avez besoin de quelque chose de rapide et de sale, pas tant pour la production que pour récupérer des données pour un utilisateur non technique, vous pouvez le coller dans la 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
Alors faites: User.select(:id,:name).all.to_csv
Si vous deviez passer à la production, je transformerais probablement cela en décorateur autour d'ActiveRecord :: Relation et plus précisément en veillant à l'ordre des champs/attributs.
Encore une autre réponse similaire, mais voici ce que je fais habituellement.
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
Au lieu de pirater le module existant, je mettrais généralement ce code dans la classe ApplicationRecord
, la classe de base de tous les modèles (généralement).
Si des détails supplémentaires sont nécessaires, j'ajouterais un paramètre nommé à la méthode to_csv
et gérerais ces fonctionnalités autant que possible dans cette classe.
De cette façon, la méthode to_csv
sera disponible à la fois pour Model et sa relation. Par exemple.
User.where(role: :customer).to_csv
# => gets the csv string of user whose role is :customer
avec Julia_builder vous pouvez configurer assez facilement une exportation 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
et alors
users = User.all
UserCsv.build(users)
On peut aussi utiliser le moteur SQL pour cela. Par exemple. pour 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
Si vous êtes sur CentOS 7, il est livré avec sqlite sorti en 2013. Cette version ne connaissait pas encore separator
et once
. Vous devrez donc peut-être télécharger le dernier binaire à partir du site Web: https://sqlite.org/download.html installez-le localement et utilisez le chemin complet de l'installation locale:
~/.local/bin/sqlite3 -init lib/tasks/export-submissions.sql db/development.sqlite3 .exit