в rails, как возвращать записи в виде csv-файла
-
01-07-2019 - |
Вопрос
У меня есть простая таблица базы данных под названием "Записи".:
class CreateEntries < ActiveRecord::Migration
def self.up
create_table :entries do |t|
t.string :firstName
t.string :lastName
#etc.
t.timestamps
end
end
def self.down
drop_table :entries
end
end
Как мне написать обработчик, который вернет содержимое таблицы записей в виде CSV-файла (в идеале таким образом, чтобы он автоматически открывался в Excel)?
class EntriesController < ApplicationController
def getcsv
@entries = Entry.find( :all )
# ??? NOW WHAT ????
end
end
Решение
Существует плагин под названием FasterCSV, который прекрасно справляется с этим.
Другие советы
FasterCSV это определенно правильный путь, но если вы хотите обслуживать его непосредственно из своего приложения Rails, вам также потребуется настроить некоторые заголовки ответов.
У меня есть метод для настройки имени файла и необходимых заголовков:
def render_csv(filename = nil)
filename ||= params[:action]
filename += '.csv'
if request.env['HTTP_USER_AGENT'] =~ /msie/i
headers['Pragma'] = 'public'
headers["Content-type"] = "text/plain"
headers['Cache-Control'] = 'no-cache, must-revalidate, post-check=0, pre-check=0'
headers['Content-Disposition'] = "attachment; filename=\"#{filename}\""
headers['Expires'] = "0"
else
headers["Content-Type"] ||= 'text/csv'
headers["Content-Disposition"] = "attachment; filename=\"#{filename}\""
end
render :layout => false
end
Использование этого позволяет легко установить что-то подобное в моем контроллере:
respond_to do |wants|
wants.csv do
render_csv("users-#{Time.now.strftime("%Y%m%d")}")
end
end
И иметь вид, который выглядит примерно так:(generate_csv
это от FasterCSV)
UserID,Email,Password,ActivationURL,Messages
<%= generate_csv do |csv|
@users.each do |user|
csv << [ user[:id], user[:email], user[:password], user[:url], user[:message] ]
end
end %>
Я принял (и проголосовал за!) Ответ @Brian за то, что он первым указал мне на FasterCSV.Затем, когда я погуглил, чтобы найти драгоценный камень, я также нашел довольно полный пример по адресу эта вики-страница.Собрав их вместе, я остановился на следующем коде.
Кстати, команда для установки драгоценного камня такова:sudo gem устанавливает fastercsv (все в нижнем регистре)
require 'fastercsv'
class EntriesController < ApplicationController
def getcsv
entries = Entry.find(:all)
csv_string = FasterCSV.generate do |csv|
csv << ["first","last"]
entries.each do |e|
csv << [e.firstName,e.lastName]
end
end
send_data csv_string, :type => "text/plain",
:filename=>"entries.csv",
:disposition => 'attachment'
end
end
Другой способ сделать это без использования FasterCSV:
Требуется CSV-библиотека ruby в файле инициализатора, таком как config/initializers/dependencies.rb
require "csv"
В качестве некоторой предпосылки следующий код основан на Расширенная форма поиска Райана Бейта это создает поисковый ресурс.В моем случае метод show поискового ресурса вернет результаты ранее сохраненного поиска.Он также реагирует на csv и использует шаблон представления для форматирования желаемых выходных данных.
def show
@advertiser_search = AdvertiserSearch.find(params[:id])
@advertisers = @advertiser_search.search(params[:page])
respond_to do |format|
format.html # show.html.erb
format.csv # show.csv.erb
end
end
Файл show.csv.erb выглядит следующим образом:
<%- headers = ["Id", "Name", "Account Number", "Publisher", "Product Name", "Status"] -%>
<%= CSV.generate_line headers %>
<%- @advertiser_search.advertisers.each do |advertiser| -%>
<%- advertiser.subscriptions.each do |subscription| -%>
<%- row = [ advertiser.id,
advertiser.name,
advertiser.external_id,
advertiser.publisher.name,
publisher_product_name(subscription),
subscription.state ] -%>
<%= CSV.generate_line row %>
<%- end -%>
<%- end -%>
На html-версии страницы отчета у меня есть ссылка для экспорта отчета, который просматривает пользователь.Ниже приведена ссылка_to, которая возвращает csv-версию отчета:
<%= link_to "Export Report", formatted_advertiser_search_path(@advertiser_search, :csv) %>
Взгляните на FasterCSV драгоценный камень.
Если все, что вам нужно, - это поддержка Excel, вы также можете рассмотреть возможность создания xls напрямую.(Смотрите таблицу::Excel)
gem install fastercsv
gem install spreadsheet-excel
Я нахожу эти параметры хорошими для открытия CSV-файла в Windows Excel:
FasterCSV.generate(:col_sep => ";", :row_sep => "\r\n") { |csv| ... }
Что касается части ActiveRecord, то для этого подойдет что-то вроде этого:
CSV_FIELDS = %w[ title created_at etc ]
FasterCSV.generate do |csv|
Entry.all.map { |r| CSV_FIELDS.map { |m| r.send m } }.each { |row| csv << row }
end
Вам нужно установить заголовок Content-Type в своем ответе, а затем отправить данные.Content_Type (Тип содержимого):приложение / vnd.ms-excel должно сделать свое дело.
Вы также можете захотеть настроить заголовок Content-Disposition таким образом, чтобы он выглядел как документ Excel, а браузер выбирал приемлемое имя файла по умолчанию;это что-то вроде Содержания-Диспозиции:привязанность;filename="#{предлагаемое имя_имя}.xls"
Я предлагаю использовать ruby gem fastercsv для генерации вашего CSV-файла, но есть также встроенный CSV-файл.Пример кода fastercsv (из документации gem) выглядит следующим образом:
csv_string = FasterCSV.generate do |csv|
csv << ["row", "of", "CSV", "data"]
csv << ["another", "row"]
# ...
end
Следующий подход хорошо сработал в моем случае и заставляет браузер открывать соответствующее приложение для типа CSV после загрузки.
def index
respond_to do |format|
format.csv { return index_csv }
end
end
def index_csv
send_data(
method_that_returns_csv_data(...),
:type => 'text/csv',
:filename => 'export.csv',
:disposition => 'attachment'
)
end
попробуйте хороший gem для генерации CSV из Rails https://github.com/crafterm/comma
Взгляните на драгоценный камень CSV Shaper.
https://github.com/paulspringett/csv_shaper
Он имеет хороший DSL и действительно хорошо работает с моделями Rails.Он также обрабатывает заголовки ответов и позволяет настраивать имя файла.
Если вы просто хотите получить базу данных csv самостоятельно с консоли, вы можете сделать это в нескольких строках
tags = [Model.column_names]
rows = tags + Model.all.map(&:attributes).map(&:to_a).map { |m| m.inject([]) { |data, pair| data << pair.last } }
File.open("ss.csv", "w") {|f| f.write(rows.inject([]) { |csv, row| csv << CSV.generate_line(row) }.join(""))}