I have #people variable contains data look exactly like:
What I have
[{"id"=>1, "name"=>"Tom", "age"=>"25"},
{"id"=>2, "name"=>"Marry", "age"=>"27"},
{"id"=>3, "name"=>"Jack", "age"=>"28"}]
and I am trying to convert the data to the CSV file, which might look like
What I am aiming to do
id | name | age
1 | Tom | 25
2 | Marry| 27
3 | Jack | 28
but my code generates a CSV file with displaying all of the data inline. For example:
What I get
{"id"=>1,"name"=>"Tom","age"=>25},{"id"=>2, "name"=>"Marry",
"age"=>"27"},{"id"=>3, "name"=>"Jack", "age"=>"28"}
What I did:
to_csv action look like:
def to_csv
CSV.generate(option) do |csv|
csv << column_names
values.each do |value|
csv << value.attributes.values_at(*column_names)
end
end
end
I added this respond_to block to one of my actions:
respond_to do |format|
format.html
format.csv {
send_data #people.to_csv,
filename: "export.csv",
type: 'text/csv; charset=utf-8'
}
I am getting data(e.g {"id"=>1,"name"=>"Tom","age"=>25 ...) from external so I pushed that data into #people variable to use. Any help would be very appreciated.
I think you need to write the function differently:
def to_csv(people)
CSV.generate do |csv|
return unless people.count > 0
csv << people[0].keys
people.each do |person|
csv << person.values
end
end
end
I suppose you could even do:
def to_csv(people)
CSV.generate do |csv|
return unless people.count > 0
csv << people[0].keys
csv << people.each.map{|item| item.values}
end
end
And then call it as a function:
respond_to do |format|
format.html
format.csv {
send_data to_csv(#people),
filename: "export.csv",
type: 'text/csv; charset=utf-8'
}
you Don't need to do like value.attributes.values_at(*column_names) just you need to do value.values which will give you the array of values that you want to store it in CSV.
for example
people = [{"id"=>1, "name"=>"Tom", "age"=>"25"},
{"id"=>2, "name"=>"Marry", "age"=>"27"},
{"id"=>3, "name"=>"Jack", "age"=>"28"}]
def to_csv
CSV.generate(headers: true) do |csv|
csv << column_names
people.each do |person|
csv << person.values
end
end
end
Here is how i do mine,
in my controller i added
respond_to do |format|
format.html
format.csv { send_data #people.to_csv }
end
then
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << column_names
all.each do |project|
csv << people.attributes.values_at(*column_names)
end
end
end
then dont forget to require 'csv' in your config/application.rb
then you can link to your csv with something like <%= link_to "CSV", people_path(format: "csv") %>
Related
Since I ended up using a csv method and not a json method I thought it might be a good Idea to change the question name and remove the part with the json if other people might have the same problem someday.
Update: I've tried using this as a way to export data.
In my controller I have the following:
def profile
#user = User.find(params[:id])
#user_posts = #user.posts.order('created_at DESC')
respond_to do |format|
format.html
format.csv { send_data #user.csv, filename: "userinfo-#{Date.today}.csv" }
end
end
In my view :
<p><%= link_to("Generate Report", user_path(#user, format: :csv), class: "btn btn-success",) %></p>
In my user.rb :
def csv
CSV.generate do |csv|
csv << column_names
all.each do |item|
csv << item.attributes.values_at(*column_names)
end
end
end
Update 2: Managed to fix it myself, I was using a wrong csv method in my Controller. Replacing it with the following :
def csv
CSV.generate do |csv|
csv << %w{ user_id user_username user_email }
csv << [ self.id, self.username, self.email]
end
end
works like a charm.
Best regards
I want to recover data in two different tables and put the data on excel but I have a little bug if someone has an idea. Thank you
class ExportController < ApplicationController
def index
#attachments= Attachment.all
#projects= Project.all
respond_to do |format|
format.html
format.csv {send_data #attachments.to_csv}
format.xls {send_data #attachments.to_csv(col_sep: "\t") and send_data #projects.to_csv(col_sep: "\t")}
end
end
end
route.rb
get '/export', to: 'export#index'
model..
class Export < ActiveRecord::Base
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << column_names
all.each do |table|
csv << table.attributes.values_at(*column_names)
end
end
end
end
view
<h1>Exportation</h1>
<%= link_to 'Download as .xlsx', export_path(format: :xls) %>
model (project.rb)
class Project < ActiveRecord::Base
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << column_names
all.each do |project|
csv << project.attributes.values_at(*column_names)
end
end
end
end
model (attachment.rb)
class Attachment < ActiveRecord::Base
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << column_names
all.each do |table|
csv << table.attributes.values_at(*column_names)
end
end
end
end
error image
You can't respond twice one request. An option would be:
format.xls {send_data #attachments.to_csv(col_sep: "\t") + #projects.to_csv(col_sep: "\t") }
With this, you'll respond with one file containing attachments and projects.
It is partly working. It generates the file but the output is not how I want it.
Controller
#messages = Message.take(2)
respond_to do |format|
format.html
format.csv { send_data #messages.to_csv }
end
Message.rb
def self.to_csv
CSV.generate do |csv|
csv << Message.attribute_names
Message.all.each do |message|
csv << message.attributes.values
end
end
end
I get the CSV file downloaded, it contains the records itself but it does not show the columns and values
#<Message:0x007fca7a028338>,#<Message:0x007fca79a6bf58>
I would expect the Message attributes like:
ID,text
1,hello
2,world
Message.take(2) returns Array. You need ActiveRecord::Relation.
Try Message.limit(2)
I'm building a marketplace app so sellers can list items to sell. I'm trying to download a CSV by seller so sellers can download their own listings.
I'm following the code in this railscast. I got the railscasts code to work as-is. It downloads all the data from my model into a csv. But when I add a seller filter to the query, it doesn't apply the filter.
In listings_controller:
def index
#listings = Listing.not_expired.order("created_at DESC")
respond_to do |format|
format.html
format.csv { send_data #listings.to_csv }
end
end
def seller
#listings = Listing.where(user: current_user).order("created_at DESC")
respond_to do |format|
format.html
format.csv { send_data #listings.seller_csv }
end
end
in listing.rb:
def self.to_csv
CSV.generate do |csv|
csv << column_names
all.each do |listing|
csv << listing.attributes.values_at(*column_names)
end
end
end
def self.seller_csv
CSV.generate do |csv|
csv << column_names
where(user: current_user).each do |listing|
csv << listing.attributes.values_at(*column_names)
end
end
end
The to_csv methods works fine and downloads all listings. But the seller_csv method also downloads all listings. I need it to filter by current_user. What am I missing?
Make your function take a list of listings as parameter.
def self.to_csv(listings)
CSV.generate do |csv|
csv << column_names
listings.each do |listing|
csv << listing.attributes.values_at(*column_names)
end
end
end
Then you cane re-use the same function in the two scenarios
def index
#listings = Listing.not_expired.order("created_at DESC")
respond_to do |format|
format.html
format.csv { send_data Listing.to_csv(#listings) }
end
end
def seller
#listings = Listing.where(user: current_user).order("created_at DESC")
respond_to do |format|
format.html
format.csv { send_data Listing.to_csv(#listings) }
end
end
Your code didn't make really sense as you were fetching listings in your controller but never re-used those fetched objects and was re-calling DB in your model's static functions.
I am trying to download some model data from a search page to a csv file. Here is the relevant code:
config/application.rb
require 'csv'
controller
def find_by_registration_date
#registration_date = params[:registration_date]
#registrations = Registration.where("DATE(created_at) = ? ", #registration_date)
respond_to do |format|
format.html
format.csv { send_data Registrations.to_csv(#registrations) }
end
end
model
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << column_names
all.each do |registration|
csv << registration.attributes.values_at(*column_names)
end
end
end
EDIT: When I try this I get the error:
TypeError in RegistrationsController#find_by_registration_date
can't convert ActiveRecord::Relation into Hash
Rails.root: /home/johnmlocklear/railsApps/nso
Application Trace | Framework Trace | Full Trace
app/models/registration.rb:43:in `to_csv'
app/controllers/registrations_controller.rb:139:in `block (2 levels) in find_by_registration_date'
app/controllers/registrations_controller.rb:137:in `find_by_registration_date'
EDIT: This is so odd. If I do something like:
def find_by_registration_date
#registrations = Registration.order :first_name
respond_to do |format|
format.html
format.csv { send_data #registrations.to_csv }
end
end
...then it works as expected. I'm assuming that one of these methods is returning an array, and another is returning a hash?
Here is what I ended up with...
app/views/registrations/find_by_registration_date.html.erb
<%= link_to 'Export CSV', csv_path(registration_date: #registration_date) %>
routes.rb
match 'csv' => 'registrations#export_csv', :as => :csv
app/controllers/registrations_controller.rb
def export_csv
#registration_date = params[:registration_date]
#registrations = Registration.where("DATE(created_at) = ? ", #registration_date)
csv = CSV.generate do |csv|
csv << ["last_name", "id"]
#registrations.each do |r|
csv << [r.last_name, r.student_id]
end
end
send_data(csv, :type => 'test/csv', :filename => 'add_hoc.csv')
end
Goes against the 'skinny controller/fat model' paradigm, but works for now.