Rails' 导出 csv 函数 "undefined method `export' for nil:NilClass"

Rails' export csv function "undefined method `export' for nil:NilClass"

我正在 Rails 存储库的 Ruby 中导出到 csv 文件的功能,我快完成了。但是,当我按下“全部导出”按钮时,出现 undefined method `export' for nil:NilClass 错误。日志显示 format.csv { send_data @foos.export, filename: "foos-#{Date.today}.csv" } 出错了。请问我错过了什么?

这是模特

class Foo < ApplicationRecord
  has_many :bars 

  def export
    [id, name, foos.map(&:name).join(' ')]
  end
end

这是控制器的一部分

  def index
    @foos = Foo.all
  end

  def export
    all = Foo.all
    attributes = %w{name}
    CSV.generate(headers: true) do |csv|
      csv << attributes

      all.each do |foo|
        csv << attributes.map{ |attr| foo.send(attr) }
      end

      respond_to do |format|
          format.csv { send_data @foos.export, filename: "foos-#{Date.today}.csv" }
      end
    end
  end

  def name
    "#{foo_id} #{name}"
  end

这是视图

<a href="/export.csv"><button class="btn btn-success">export all</button></a>

这是路线

Rails.application.routes.draw do
  resources :foos
  
  get :export, controller: :foos

  root "foos#index"
end

这是耙子 (lib/tasks/export.rb)

namespace :export do
    task foo: :environment do
      file_name = 'exported_foo.csv'
      csv_data = Foo.to_csv
   
      File.write(file_name, csv_data)
    end
  end

首先创建一个 service object 来收集记录和 returns CSV,以便您可以单独测试 CSV 生成:

# app/services/foo_export_service.rb
# Just a Plain Old Ruby Object that converts a collection of foos into CSV
class FooExportService
  # The initializer gives us a good place to setup our service
  # @param [Enumerable] foo - an array or collection of records
  def initialize(foos) 
    @headers = %w{name} # the attributes you want to use
    @foos = foos
  end

  # performs the actual work
  # @return [String]
  def perform
     CSV.generate do |csv|
       @foos.each do |foo|
         csv << foo.serializable_hash.slice(@headers).values
       end
     end
  end

  # A convenient factory method which makes stubbing the 
  # service easier
  # @param [Enumerable] foos - an array or collection of records
  # @return [String]
  def self.perform(foos)
    new(foos).perform
  end
end
# example usage
FooExportService.perform(Foo.all)

并非 Rails 应用程序中的所有内容都需要塞入模型、视图或控制器中。他们已经有足够的责任。如果您确实需要,这还可以让您在 rake 任务中重新使用代码。

这只是遍历集合并使用 Rails 内置序列化功能将模型实例转换为可以序列化为 CSV 的哈希值。它还使用了 Hash#slice 也重新排序哈希键的事实。

在您的控制器中,您只需使用服务对象:

class FoosController
  def export
    @foos = Foo.all
    respond_to do |format|
      format.csv do 
        send_data FooExportService.perform(@foos), 
          filename: "foos-#{Date.today}.csv" 
      end
    end
  end
end 

您一开始甚至不需要单独的 export 操作。只需使用 MimeResponds 将 CSV 作为可用的响应格式添加到索引中:

class FoosController
  def index
    # GET /foos 
    # GET /foos.csv 
    @foos = Foo.all
    respond_to do |format|
      format.html
      format.csv do 
         send_data FooExportService.perform(@foos), 
            filename: "foos-#{Date.today}.csv" 
      end
    end
  end
end 
<%= link_to("Export as CSV", foos_path(format: :csv)) %>