0

railscasts #396 Importing CSV に従い、CSV アップロードを Rails プロジェクトに実装しました。

これは私のビューファイルです:

<%= 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: trueheaders: false、エラーが発生します。

CustomersController#import の NoMethodError

["abc@wer.com"]:Array の未定義メソッド `to_hash'

ヘッダー行を必要とせずに CSV ファイルをアップロードする方法を誰か教えてもらえますか?

4

2 に答える 2

1

CSV ファイルのアップロードと処理に関する限り、非常に近いものです。Customer.create!呼び出しを介して、データベースにデータを入力するためのデータ行の読み取りに問題があります

データが 1 行しかない CSV ファイルでテストしているようです。を使用するheaders: trueと、その 1 行がヘッダーに変換され、その後CSV.foreach反復子で無視されました。したがって、事実上、ファイルにはデータがなく、反復は発生しませんでした。入力ファイルに 2 行のデータがある場合は、とにかくエラーが発生します。

これで、 を使用する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 ファイルを受け取り、キーなしでそれを理解する方法を理解しなければならなかったことがあれば、ヘッダーが重要である理由がわかるでしょう。

于 2016-05-06T00:39:08.237 に答える
-1

CSV ファイルを配列の配列にロードし、最初の行を削除するだけです。

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

ただし、これはデータを値の配列としてのみ保存します。

使用するheaders: trueと、キーが列ヘッダー名であるハッシュが使用されます。

于 2013-07-27T21:22:46.287 に答える