Learn how to export records into CSV files using Ruby on Rails. Here are the steps.
Steps:
- Add a controller and make sure you handle the csv request.
- Add a route to point to your controller
- Add a model with the class method to_csv
Lets code it!
Controller
# users_controller.rb
class UsersController < ApplicationController
def index
@users = User.all
respond_to do |format|
format.html
format.csv { send_data @users.to_csv, filename: "users-#{Date.today}.csv" }
end
end
Routes
# routes.rb
...
resources :users, only: :index
...
Model
# user.rb
class User < ActiveRecord::Base
def self.to_csv
attributes = %w{id email name}
CSV.generate(headers: true) do |csv|
csv << attributes
all.find_each do |user|
csv << attributes.map{ |attr| user.send(attr) }
end
end
end
def name
"#{first_name} #{last_name}"
end
end
Core custom methods:
to_csv
will map out the "id, email, name" values of your model collection and then generate a CSV string.
Rails core methods for this feature:
#send_data
Sends the given binary data to the browser. This method is similar to render :text => data, but also allows you to specify whether the browser should display the response as a file attachment (i.e. in a download dialog) or as inline data. You may also set the content type, the apparent file name, and other things. Source
Top comments (11)
How about doing this as a concern instead?
Then
or
Great for DRY approach, thanks for this :)
Nitpicking if I may, a missing last or first name won't be noticed in HTML but it will be noticed in CSV when a human reads it directly or imported in Excel/LibreCalc.
Nice, but there's a mistake in the controller/ model. The code appears to work because @users=User.all which matches the set of users computed within the model class
The correct call would be User.to_csv(@users). The User model function has to be modified to take the new parameter into account.
nope, it's right the way it is. Since @users is an
ActiveRecord::Relation
so you can say@users.to_csv
How to run it in background job(Sidekiq) ?
It depends, are you going to send an email on a background job? or are you going to upload the file on S3? please give some context.
from my experience, when large amounts of data is involved, the server times out. Im thinking if it is possible to move the CSV export and show a download link to the user?
Ok, then you will need to do the following:
UPDATE: I was using
#each
, I changed it to#find_each
which saves memory. See api.rubyonrails.org/classes/Active...But this will not be a report friendly, find_each forces the sequence to be order by id desc :( thanks to the postoverall!