我今天开始阅读Rails Antipatterns,我想将其中一些实践付诸实践。我正在重构我最初在控制器中构建的CSV导出。由于这是一个不好的做法,我将其考虑到模型......然后是自己的模型。这样我就可以将该方法重用于其他目的。
我有一个使用以下方法的模型:
#app/models/imagery_request.rb
class ImageryRequest < ActiveRecord::Base
def convert
ImageryRequestConverter.new(self)
end
end
我有另一个这样的模型:
#app/models/imagery_request_converter.rb
class ImageryRequestConverter
attr_reader :imagery_requests
def initialize(imagery_requests)
@imagery_requests = imagery_requests
end
def to_csv
csv_string = FasterCSV.generate do |csv|
# header row
csv << ["id", "service_name", "description", "first_name", "last_name", "email", "phone_contact", "region",
"imagery_type", "file_type", "pixel_type", "total_images",
"tile_size", "progress", "expected_date", "high_priority", "priority_justification",
"raw_data_location", "service_overviews", "is_def",
"isc_def", "special_instructions", "navigational_path", "FY Queue",
"created_at", "updated_at"]
# data rows
@imagery_requests.each do |ir|
csv << [ir.id, ir.service_name, ir.description, ir.first_name, ir.last_name, ir.email,
ir.phone_contact, ir.region, ir.imagery_type, ir.file_type, ir.pixel_type,
ir.total_images, ir.tile_size, ir.progress, ir.expected_date, ir.high_priority,
ir.priority_justification, ir.raw_data_location, ir.service_overviews,
ir.is_def, ir.isc_def, ir.special_instructions, ir.navigational_path,
ir.fyqueue, ir.created_at, ir.updated_at
]
end
# send it to the browser with proper headers
send_data csv_string,
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=Imagery_Requests-#{Time.now.strftime("%Y%m%d")}.csv"
end
end
end
当我尝试在我的视图中引用它时:
<%= link_to @imagery_requests.convert.to_csv %>
我收到错误:
undefined method `convert' for #<ActiveRecord::Relation:0x21f966d0>
如何调用此方法?
答案 0 :(得分:0)
@imagery_requests
变量实际上是一个Relation
对象,一旦被调用,它将始终是一个记录集合。您正在调用此对象上的实例方法,因为您在整个集合上调用它而不是此集合中的对象,所以该方法无效。
除此之外,调用link_to
中的方法就像你在那里做的那样,不会全力以赴。该链接应转到控制器操作,该操作将解析这些请求并正确返回CSV。
答案 1 :(得分:0)
跟随Ryan Bigg的回答
您有两个问题:
要调用convert方法,您需要在集合中指定一个对象。例如。 @imagery_requests.first.convert.to_csv
或@imagery_requests[i].convert.to_csv
等
您无法获得文件内容的链接,这是您的代码尝试执行的操作。相反,您需要链接到一个新操作,例如将返回csv的download_csv。
由于新操作(下载文件的csv版本)不是标准restful套件的一部分,因此您需要向资源添加其他操作。 Eg article
用户体验(UX)选择:
您可以为ImageryRequests
集合的每个成员创建一个下载为csv操作,这意味着想要5个请求的csv版本的人需要下载5个不同的csv文件。
或者您可以为集合创建下载。但每个http请求都有一个响应。通常的解决方案:使用多个文件将zip文件返回给客户端。
在任何情况下,您都应该将代码移动到模型中并移出控制器。
EMail UX解决方案如果创建csv文件花费超过一秒钟,这个特别好 - 因为Rails是单线程的,所有响应应该非常快。
csv下载的表单应该使用电子邮件地址作为参数,而不是使用文件进行响应。然后使用DelayJob或其他调度程序在后台通过电子邮件发送csv文件。
您可以将zip文件作为电子邮件附件发送,也可以发送多个csv文件,因为电子邮件可以包含多个附件。
提示:您的表单应处理多个电子邮件地址,并允许用户在电子邮件中包含封面注释。这将使请求者能够将报告发送给多个人。