Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Rails, how can I submit a form and request a csv formatted page?

I have a few parts of the solution, but I'm having trouble bringing them together.

I have a page with two text fields (in a form_tag) in which I'll enter a datetime string with the start and end dates of the records I want to download in CSV form.

I can use a submit_tag and get the two dates, but then I don't know how to get the view to tell the controller that I want a CSV, so . I can use a link_to, but then the params get left behind.

The view and controller look a little wonky as I'm trying to figure out how this stuff should work together. I won't ship both a link and a button, for example. I also removed/changed things as needed for brevity/privacy.

show.html.erb:

<%= form_tag do %>    
  <br/><br/>
  <%= label_tag :start_date, "From:" %>
  <%= text_field_tag :start_date, nil, size: 40 %>
  <%= label_tag :end_date, "To:" %>
  <%= text_field_tag :end_date, nil, size: 40 %>
  <br/>

  <%= link_to "Export Report", report_path(:csv) %>

  <%= submit_tag("Generate .CSV", format: :csv) %><br/><br/>
<% end %>

report_controller.rb:

require 'csv'

class ReportController < ApplicationController
  def show
    if params[:start_date]          
      @data = get_data(params[:start_date], params[:end_date])
      respond_to do |format|
        format.html
        format.csv
      end
    end
  end

  def build_csv_enumerator(header, data)
    Enumerator.new do |y|
      CSVBuilder.new(header, data, y)
    end
  end

  def download
    if params[:start_date]
      @data = get_data(params[:start_date], params[:end_date])
      respond_to do |format|
        format.html
        format.csv
      end
    end
    redirect_to action: "show"
  end

  private def csv_filename
    "report-#{Time.now.to_s}.csv"
  end
end

class CSVBuilder

  attr_accessor :output, :header, :data

  def initialize(header, data, output = "")
    @output = output
    @header = header
    @data = data
  end

  def build
    output << CSV.generate_line(header)
    data.each do |row|
      output << CSV.generate_line(row)
    end
    output
  end
end

download.csv.erb:

<%- headers = ["name", "email", "created_at"] -%>
<%= CSV.generate_line(headers) %>
<%- @data.each do |line| -%>
  <%- row = line.values_at(*headers) -%>
  <%= CSV.generate_line(row) %>
<%- end -%>
like image 421
Roderick Avatar asked Jun 29 '17 00:06

Roderick


People also ask

What is Form_for in Ruby?

This is the name used to generate the input's name (and the params' names), example: = form_for Admin.new, as: :user do |f| #^^^^ = f.input :username # will generate an input like this: <input type='text' name='user[username]' #... /> #

What is form tag in Ruby on Rails?

This can be used for: creating new database records, building a contact form, integrating a search engine field, and pretty much every other aspect of the application that requires user input.


2 Answers

Because you want to download a CSV, the request should be a GET request.

Your link should look like <a download href="/reports/1.csv?from=2017-01-01&to=2017-12-31">Download</a>. I suggest you build this url on browsers (which means using javascript, not rails).

Your rails application must understand the extension .csv. This can be configured in config/initializers/mime_types.rb

Mime::Type.register "text/csv", :csv

Now you can send CSV to the browser

class ReportsController < ApplicationController
  def show
    respond_to do |format|
      # ...
      format.csv do
        @csv = ...
        send_data @csv.to_s  # what you send must be a string
      end
    end
  end
end
like image 50
Aetherus Avatar answered Sep 19 '22 13:09

Aetherus


Links are self-contained; submit buttons (typically) submit forms (with all their non-disabled, name-having values) to the action URL of the form (unless you're doing submission through JavaScript, in which case you can do whatever you want, obviously).

Thus, you don't specify the URL on submit_tag, you specify it on form_tag:

Starts a form tag that points the action to a url configured with url_for_options just like ActionController::Base#url_for.

like image 26
Amadan Avatar answered Sep 18 '22 13:09

Amadan