【问题标题】:Rails: Upload CSV file without headerRails:上传没有标题的CSV文件
【发布时间】:2013-07-27 19:48:15
【问题描述】:

我遵循 railscasts #396 Importing CSV 并在我的 rails 项目中实现了 CSV 上传。

这是我的视图文件:

<%= form_tag import_customers_path, multipart: true do %>
  <%= file_field_tag :file %>
  <%= submit_tag "Import" %>
<% end %>

这是我的控制器操作:

def import
  current_user.customers.import(params[:file])
  redirect_to customers_path, notice: "Users imported."
end

这些是我的模型方法:

def self.to_csv(options = {})
    CSV.generate(options) do |csv|
        csv << column_names
        all.each do |customer|
            csv << customer.attributes.values_at(*column_names)
        end
    end
end

def self.import(file)
  CSV.foreach(file.path, headers: true) do |row|
    Customer.create! row.to_hash
  end
end

这里我不希望用户在 CSV 中包含标题。当我将headers: true 替换为headers: false 时,出现错误:

CustomersController#import 中的 NoMethodError

["abc@wer.com"]:Array 的未定义方法 `to_hash'

谁能告诉如何在不需要标题行的情况下上传 CSV 文件?

【问题讨论】:

    标签: ruby-on-rails ruby csv file-upload ruby-on-rails-3.2


    【解决方案1】:

    就 CSV 文件的上传和处理而言,您已经非常非常接近了。您只是在通过Customer.create! 调用读取数据行以填充数据库时遇到问题

    您似乎一直在使用只有一行数据的 CSV 文件进行测试。使用headers: true,该单行被转换为标题,随后在CSV.foreach 迭代器中被忽略。因此,实际上,文件中没有数据,也没有发生迭代。如果输入文件中有两行数据,无论如何你都会遇到错误。

    现在,当您使用headers: false 时,该行数据将被视为数据。这就是问题所在:没有正确处理数据。

    由于您的问题中没有架构,我假设在字段上有一点余地;您应该能够很容易地推断以使其适用于您的情况。这段代码展示了它是如何工作的:

    CSV.parse(csv_data, headers: false) do |row|
      hash = {
        first_name: row[0],
        last_name: row[1],
        age: row[2],
        phone: row[3],
        address: row[4]
      }
      Customer.create!(hash)
    end
    

    如果您想要一个带有标题的 CSV 版本,这在这种情况下会很好用,并且具有不允许任意访问不应从外部来源分配的列的好处:

    CSV.parse(csv_data, headers: true, header_converters: :symbol) do |row|
      hash = {
        first_name: row[:first_name],
        surname: row[:last_name],
        birth_year: Date.today - row[:age],
        phone: row[:phone],
        street_address: row[:address]
      }
      Customer.create!(hash)
    end
    

    请注意,您模型中的Customer#to_csv 也不完全正确。首先,它会创建带有标题的 CSV 文件,因此您将无法使用此实现导出然后再次导入。接下来,标题字段变量column_names实际上并未在此代码中定义。最后,代码不控制写入 CSV 的列的顺序,这意味着标题和值可能不同步。一个正确的(非标题)版本非常简单:

    csv_data = CSV.generate do |csv|
      csv.each do |customer|
        csv << [customer.first_name, customer.last_name, customer.age, customer.phone, customer.address]
      end
    end
    

    基于标题的版本是这样的:

    csv_data = CSV.generate do |csv|
      csv << ["First Name","Last Name","Age","Phone","Address"]
      csv.each do |customer|
        csv << [customer.first_name, customer.last_name, customer.age, customer.phone, customer.address]
      end
    end
    

    就我个人而言,我会使用基于标题的版本,因为它更加健壮,并且很容易理解哪些列是哪些列。如果您曾经收到过无标题的 CSV 文件,并且不得不弄清楚如何在没有任何键的情况下理解它,那么您就会知道为什么标题很重要。

    【讨论】:

      【解决方案2】:

      您可以将 CSV 文件加载到数组数组中并删除第一行:

      data = CSV.read("path/to/file.csv")
      data = data[1..-1]
      

      但是,这只会将数据存储为值数组。

      当您使用headers: true 时,它使用哈希,其中键是列标题名称。

      【讨论】:

        猜你喜欢
        • 2012-11-26
        • 1970-01-01
        • 2016-03-25
        • 2018-10-10
        • 1970-01-01
        • 2014-08-20
        • 2020-02-02
        • 1970-01-01
        • 2012-05-08
        相关资源
        最近更新 更多