I am working on a rails app which reads the JSON data then creates a CSV from that data. The issue I am facing is the CSV download works perfectly fine but the index page doesn't show.
When I go to localhost:3000 the CSV is downloaded but index.html.erb page is not rendered. Please help me find the issue where I am going wrong.
controller.rb
def index
csv_data = CSV.generate do |csv|
file = JSON.parse(File.open("app/assets/javascripts/data.json").read)
#data = file
new_file = #data.sort_by!{ |m| m["name"] }
new_file.each do |hash|
hash['name'] = (hash.values[2])
hash['city'] = (hash.values[3])
end
end
send_data csv_data, type: :csv
end
routes.rb
root :to => "name#index"
index.html.erb
<h1 align="center">File is downloaded</h1>
if I add the code below in the controller index action then the html is rendered but the download csv stops working.
respond_to do |format|
format.html
format.csv { send_data csv_data, type: :csv }
end
You need to add html format as respond format:
def index
csv_data = ..
respond_to do |format|
format.html
format.csv { send_data csv_data }
end
end
1/ Extract your CSV logic in a private method:
def download_csv
csv_data = CSV.generate do |csv|
file = JSON.parse(File.open("app/assets/javascripts/data.json").read)
#data = file
new_file = #data.sort_by!{ |m| m["name"] }
new_file.each do |hash|
hash['name'] = (hash.values[2])
hash['city'] = (hash.values[3])
end
end
send_data csv_data, type: :csv
end
2/ Call your private method inside before_action:
before_action :download_csv, only: :index
3/ Update your route to set html by default (maybe not necessary):
root :to => "name#index", defaults: { format: 'html' }
Basically calling send_file/send_data from a before action will not halt the request cycle, because it doesn't use render or redirect_to.
I hope it helps.
__Edited answer__
Actually I guess is not a regular comportement to render multiple routes for one action. And I think you should find another way to achieve this. I recommend you the following code (from my first solution):
1/ Add a specific route to download
get '/name/download_csv', to: "name#download_csv"
2/ Add some Jquery in your view
<script>
$(document).ready(function() {
window.location.href = 'name/download_csv.csv';
})
</script>
3/ Remove the private reserved word from your controller
In this way each time you go to your index view you are going to call your download action throughout Jquery.
It's not really "rails way" but it should works.
Let me know if it works.
Related
I'm having a little trouble downloading values to a CSV file. I have two models, downtowns and properties. Downtowns have many properties. Properties each have one downtown. Problem i'm having is that with what i've done i've been able to download all properties, which is cool! But what I'm struggling with is downloading only properties owned by the downtown.
To what I have initially to download all properties I have
In my properties controller:
def index
#properties = Property.all
respond_to do |format|
format.html
format.csv { send_data #properties.to_csv }
end
end
In my properties model
def self.to_csv
attributes = %w(id name owner_first_name created_at)
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |property|
csv << property.attributes.values_at(*attributes)
end
end
end
and then in my view I have.
= link_to "download CSV", downtown_properties_path(:format => :csv), class: "button"
So again this works, but because of my associations the view where i can add my link can only be in downtowns/1/properties which based on my putting the code in the index this actually makes sense
I tried something along the lines of
#download_properties = Property.where(downtown: #downtown_id)
respond_to do |format|
format.html
format.csv { send_data #download_properties.to_csv }
end
However #downtown_id is not defined at this point. So I think the solution is better in the downtown model?
If anyone has any idea looking through this and able to help me out, I would greatly appreciate it!
shouldn't it be:
#download_properties = Property.where(downtown: params[:id])
or
#download_properties = Downtown.find(params[:id]).properties
I have a download button in my view form, which use an "index" action in the controller
def index
respond_to do |format|
format.html { #some_variables = some_variables.page(params[:page]) }
format.csv {
render csv: some_variables, headers: SomeVariable.csv_headers
}
end
end
And some def self.to_csv method in my model SomeVariable, in which I generate the CSV file.
Is there anyway to check if the download was OK and for example set a flag.
if download went OK
do something
else
raise some error
end
I thought about "begin rescue" in a index action, but if there is any other "smarter" implementation sharing it would be more than appreciated.
send_file can be a good alternative to what you're planning to do.
You can find more information here How to download file with send_file? and at the API Dock
I figured out a simple solution, I opted for an after_action.
after_action is run only if the action before succeeds, and in this case I'm trying to do something if the download was executed i.e if the csv render get a 202.
So the if the action index is executed after that I run a method for example updating the downloaded time.
class SomeVariableController < ApplicationController
after_action :update_downloaded_time, only: :index, if: -> {current_user.admin?}
def index
respond_to do |format|
format.html { #some_variables = some_variables.page(params[:page]) }
format.csv {
render csv: some_variables, headers: SomeVariable.csv_headers
}
end
end
def update_downloaded_time
#some_varibale.update_all(downloaded_at: Time.now)
end
It's simple..
when I write it in my controller like this.
def index
#articles = News.order(:id)
respond_to do |format|
format.html
format.csv { render text: #articles.to_csv}
end
end
and in news.rb
def self.to_csv
CSV.generate do |csv|
csv << column_names
all.each do |product|
csv << product.attributes.values_at(*column_names)
end
end
end
What I expected when I open my site(http://localhost:3000/mycontroller.csv) is shows the text in my screen.
However, it just download csvfile..... Even when I changed render text to html.
What is wrong in my code?
You can try this code
class HomeController < ApplicationController
respond_to :html, :json, :csv
def index
#modelnames = Modelname.all
respond_with(#modelnames)
end
end
I'm trying to create a simple CSV export in Rails. The export works fine except when deleting/archiving table items. I'm guessing this is because the export is encountering blanks.
This is working:
= link_to transactions_path(format: :csv) do
Except when there is a item missing from the transaction.
Tried this
= link_to transactions_path(format: :csv,skip_blanks: true) do
but I still get a ERR_INVALID_RESPONSE when calling the export
TransactionController:
respond_to :html, :json, :csv
def index
#shops = current_user.shops
respond_with(#shops) do |format|
format.csv do
response.headers['Content-Type'] = 'text/csv'
response.headers['Content-Disposition'] = "attachment; filename=transactions-#{Time.now.strftime('%Y%m%d%H%M')}.csv"
render inline: #shops.to_csv
end
end
end
Any suggestions?
Change to_csv to pass skip_blanks.
#shops.to_csv(skip_blanks: true)
If you want a link to download a CSV file, you should use send_data instead. If you want to display the file in the browser, use render text: #shops.to_csv
http://railscasts.com/episodes/362-exporting-csv-and-excel
respond_to :html, :json, :csv
def index
#shops = current_user.shops
respond_with(#shops) do |format|
format.csv do
send_data #shops.to_csv, type:'text/csv', filename: "transactions-#{Time.now.strftime('%Y%m%d%H%M')}.csv"
end
end
end
Change your link_to back to what you had before.
= link_to transactions_path(format: :csv)
Maybe using send_data:
def index
respond_to do |format|
format.csv { send_data #current_user.shops.to_csv, filename: "transactions-#{Time.now.strftime('%Y%m%d%H%M')}.csv" }
end
end
Also, is the to_csv an instance method?
I'm running a Rails 4 application and am using the Ransack gem to filter results for employees. I've seen multiple examples of how to limit columns on the exported CSV file, but not on limiting rows. To my understanding, the following code should call the .to_csv method on the filtered employees, but currently all rows are being downloaded. Do I need to pass an array of the IDs of the filtered results to the .to_csv method?
View:
<h3>Download</h3>
<%= link_to "CSV", employees_path(format: "csv") %>
Controller:
def index
#q = Employee.ransack(params[:q])
#q.build_condition
#employees = #q.result(distinct: true)
respond_to do |format|
format.html
format.csv { render text: #employees.to_csv }
end
end
Model:
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << column_names
all.each do |employee|
csv << employee.attributes.values_at(*column_names)
end
end
end
I know this is a bit delayed but this is how I fixed same problem.
Change the link to have params.merge to keep the filtered results.
<%= link_to "CSV", employees_path(params.merge(format: "csv")) %>
I also had to change my format.csv to the following to get it to work:
format.csv { send_data #employees.to_csv, filename: "employees.csv" }
Hope this helps.
In this line you're adding all of the employee models.
all.each do |employee|
Instead you should have something like
def self.to_csv(employees)
..
employees.each do |employee|
..
end
And call this class method like this:
format.csv { render text: Employee.to_csv(#employees) }
(Read more about class methods here)