1
0

More than 3 years have passed since last update.

[Rails] csvファイルを使って駅のデータをデータベースに保存する方法

Last updated at Posted at 2020-04-17

はじめに

作成しているwebサービスで駅のデータが必要だったので色々調べたところ、データベースに保存できました。せっかくなので記事に残しておきます。

※筆者は初学者です。文章に誤りがあったらコメント等で教えてください。

4/18加筆 lonとlatの型を間違っていたため:integer:decimalに変更しました。

筆者の環境

Rails 5.1.7
ruby 2.5.7

※他に必要な情報ありましたら教えて下さい。

csvファイルをダウンロードしたサイト

無料のものだったり有料のものだったりありますが、
私はこのページでダウンロードしました(この記事と同じようにしたい方は駅データの2020-03-16をダウンロードしてください)。
一部の情報は有料ですが、その情報も4,400円ってすごいですね。駅データの仕様書とかも同じサイトにあるので詳しくはそこで見てね。

他のデータ配布サイトも知りたいって方はこの記事にまとめられてます。

ここでダウンロードしたファイルはdb配下にcsvディレクトリを作成後、ファイル名をstation_data.csvに変えて、アプリケーションからの相対パスがdb/csv/station_data.csvとなるように保存してください。

Railsでcsvファイルをインポート

データを入れるデータベースを準備する

まずモデルを作成します。

$ rails g model station_data #dataはdatumの複数形なので名前のつけ方が悪いかも...

次にこの時に作成されたマイグレーションファイルを駅データの型に合わせて次のように編集します。いらないと思った情報は含めないようにしているので、必要な方はそれらも追加してください。(例: 駅がある場所の郵便番号が欲しいならt.string :postを追加するなど)

db/migrate/...create_station_data.rb
class CreateStationData < ActiveRecord::Migration[5.1]
  def change
    create_table :station_data do |t|
      t.integer :station_g_cd
      t.string :station_name
      t.integer :line_cd
      t.integer :pref_cd
      t.string :address
      t.decimal :lon
      t.decimal :lat

      t.timestamps
    end
  end
end

変更をデータベースに反映させます。

$ rails db:migrate

csvファイルをインポートするためのコードを書く

lib/tasks/import.rake(新しく作成)に次のコードを記入します。

lib/tasks/import.rake
require 'csv'

# rake import:station_data
namespace :import do
  #このdescはdescribeのdesc
  desc "Import station_data from csv"

  task station_data: :environment do
    path = File.join Rails.root, "db/csv/station_data.csv"
    puts "path: #{path}"
    list = []
    CSV.foreach(path, headers: true) do |row|
      list << {
          station_g_cd: row["station_g_cd"],
          station_name: row["station_name"],
          line_cd: row["line_cd"],
          pref_cd: row["pref_cd"],
          address: row["address"],
          lon: row["lon"],
          lat: row["lat"]
      }
    end
    puts "start to create station data"
    begin
      StationDatum.create!(list) #クラス名注意
      puts "completed!!"
    rescue ActiveModel::UnknownAttributeError => invalid
      puts "raised error : unKnown attribute "
    end
  end
end

軽くコードの解説をしますとCSV.foreachpathにあるcsvファイルから1行ずつ読み込んでいます。読み込まれた行はlistという空の配列に{station_g_cd: 1110110, station_name: 函館,....}のような形のハッシュを追加していっています。

補足: headers: trueには1行目をヘッダ行として無視する役割があるそうです。

コードを書けたらターミナルで次の一文を出力して終わりです!

$ rake import:station_data 

これでdb/development.sqlite3を特定のツールで確認してみると駅のデータが反映されていると思います。お疲れ様でした。

おまけ(重複したデータの削除)

今回使用した駅データは、同じ場所の駅でも路線が違ったりすると別のレコードで記録されています(どの駅データでもそうなっていると思いますが)。私はユーザーと最寄り駅を紐付けたかったのですが、一人のユーザーが複数の駅データと紐ずくとまずいと思ったので、重複したデータを1つだけ残して他は削除しました。

コードの説明などは参考にしたサイトからお願いします。次の2つのコードできれいに削除できます。

$rails c

irb(main):001:0> hash = StationDatum.group(:station_g_cd).having('count(*) >= 2').minimum(:id)

irb(main):001:0> StationDatum.where(station_g_cd: hash.keys).where.not(id: hash.values).destroy_all

:station_g_cdは一つの駅ごとに一つだけ与えられているコードであることを利用しています。

終わりに

ここまで読んでくださってありがとうございました。

https://qiita.com/kou/items/e0d3fab106e4c52a7f26
このサイトでも紹介されてる「駅すぱあとAPI」とかを理解が進むと使ってみたいです。使って理解できたらまたまとめると思います。

参考にしたサイト

インポートのために書いたコードの意味の理解で用いました。
http://www.code-magagine.com/?p=8004

全てはこの記事と言っても過言ではない。本当に感謝。
https://qiita.com/yoshito410kam/items/40b675760bd8a1f8e728

重複したデータの削除についてはここ。
https://qiita.com/wakasa51/items/efe2b4b3554a50d18c32

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0