ローカルおよびHeroku上のデータベースにcsvファイルのデータを追加(Sinatra)


はじめに

Heroku上のデータベースにデータを格納し,それを参照するだけの簡単なSinatraアプリを作成したかったのですが,少し詰まったので整理も兼ねて記事にします.
ActiveRecordを用いずにcsvファイルからHeroku上のデータベースに格納します.
Heroku上にデータベースを設置し,そのデータベースから読み取りのみを行うWebサービスを作りたい方や初期データを格納したいといった方向けの記事です.

準備: Sinatraアプリの作成

準備として,Sinatraを含めた本記事(作成したいアプリ)で必要なパッケージのインストールを行います.

~/
$ mkdir sinatra-db
$ cd sinatra-db
$ bundle init
$ vim Gemfile
$ bundle install --path vendor/bundle --without production
Gemfile
# gem "rails"
gem "sinatra"
gem "sinatra-contrib"
gem "sinatra-activerecord", :require => 'sinatra/activerecord'
gem "activerecord", "~> 5.2.3"
gem "rake"

group :development do
    gem 'sqlite3'
end

group :production do
    gem 'pg'
    gem "activerecord-postgresql-adapter"
end

2019年9月29日,ActiveRecordの最新バージョンは6.0.0ですが,データベース作成で不具合が発生するので,動作確認の取れている5.2.3をインストールします.

これ以降,~/sinatra-db/~と表記します.
また,以下に本記事対象外のファイルを列挙します.簡単に説明すると,やっていることは単純にデータベース内のデータを表示しているだけです.
後々書くべきこともコード内には書いています.必要に応じてここに戻ってきて,見返してみてください.

~/app.rb
require 'bundler'
Bundler.require

class App < Sinatra::Base
    configure :development do
        ActiveRecord::Base.establish_connection(
            adapter: 'sqlite3',
            database: 'db/development.db'
        )
    end

    configure :production do
        ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])
    end

    get '/' do
        @comments = Comment.all
        erb :index
    end
end

class Comment < ActiveRecord::Base
end
~/views/index.erb
<% @comments.each do |comment| %>
    <div style="padding: 10px; margin-bottom: 10px; border: 1px dotted #333333;">
        <h3><%= comment.title %></h3>
        <h5><%= comment.detail %></h5>
        by <%= comment.name %>
    </div>
<% end %>
~/config.ru
require './app'
run App
~/Procfile
web: bundle exec rackup config.ru -p $PORT

準備: Herokuにアプリを作成

更に準備として,先にHeroku上にアプリのデプロイ先を作成しておきます.
ここはぱぱーっと行きます.詳しく知りたい方はブログ等を参考にしてください.

~/
# Herolu CLIはインストール済を想定
$ heroku login
...

# リポジトリの作成
$ git init
$ git add .
$ git commit -m "first commit"

# デプロイ先の作成(アプリの名前は一意じゃないとダメみたいです.お好きな名前で.)
$ heroku create (app name)

# デプロイ(後々)
$ git push heroku master

データベースの作成とレコードの追加

本記事では,以下のようなテーブルをローカルとHeroku上に作成します.
ローカルではSQLite3,HerokuではPostgreSQLを使用します.

id name title detail
1 hoge hello Hello Sinatra
2 foo foodbye Goodbye Sinatra
3 bar emergency Reply Required

以下のようなcsvファイルを用意します.

~/comments.csv
1,hoge,hello,Hello Sinatra
2,foo,goodbye,Goodbye Sinatra
3,bar,emergency,Reply Required

(1行目にid,name,title,detailのようなカラムの名前を入れたかったのですが,後々Heroku上のテーブルに追加する際にエラーが起きてしまうので入れていません.)

ローカル

データベースの作成

別の記事を参考にマイグレーションファイルからデータベース(テーブル)を作成します.こちらも詳しくは先の記事を参考にしてください.

~/
$ vim Rakefile

# 使えるコマンドの確認
$ bundle exec rake -T
...

# マイグレーションファイルの作成と編集
$ bundle exec rake db:create_migration NAME=create_comments
# => db/migrate/20190928154422_create_comments.rb
$ vim ./db/migrate/20190928154422_create_comments.rb

# データベースの作成
$ bundle exec rake db:migrate
~/Rakefile
require 'sinatra'
require 'sinatra/activerecord'
require 'sinatra/activerecord/rake'
require './app'
~/db/migrate/20190928154422_create_comments.rb
class CreateComments < ActiveRecord::Migration[5.2]
    def change
        create_table :comments do |t|
            t.string :name
            t.string :title
            t.string :detail
        end
    end
end

マイグレーションファイルにて,idをカラムとして追加していませんが,マイグレーションする際に自動でidカラムが作成されるみたいなので,ここでは追加していません.

~/dbdevelopment.dbschema.rbができたと思います.
このdevelopment.dbがデータベースの本体です.

csvファイルからレコードの追加

データベースにSQLite3で直接接続し,コマンドでcsvファイルからレコードを追加します.
と言っても,コマンドを数ステップ打つだけでできてしまいます.

~/
$ sqlite3 ./db/development.db
...

sqlite> .schema comments
CREATE TABLE IF NOT EXISTS "comments" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "title" varchar, "detail" varchar);

sqlite3> .separator ,
sqlite3> .import comments.csv comments

sqlite3> select * from comments;
1|hoge|hello|Hello Sinatra
2|foo|goodbye|Goodbye Sinatra
3|bar|emergency|Reply Required

sqlite3> .quit

無事レコードを追加することができました.
Sinatraアプリの方でも呼び出せるか確認してみましょう.
bundle exec rackup config.ruと打ち,localhost:9292にブラウザでアクセスしてみます.

Sinatra(ActiveRecord)の方でもレコードを呼び出すことができています.
これで,ローカルでのデータベースの構築は完了です.

Heroku上

次にHeroku上でデータベースの作成からcsvファイルのレコードの追加を行ってみます.

データベースの作成

Herokuでは,AddonとしてHeroku Postgresが提供されています.
これの無料プラン(hobby-dev)をアプリに追加して使用します.

~/
$ heroku addons:create heroku-postgresql:hobby-dev

これだけで追加できました.
heroku pg:infoで情報を見たり,heroku pg:psqlで作成したデータベースにアクセスできます.
詳しくは,公式リファレンスこちらのサイトも参考にしてください.

次にテーブルの作成です.と言っても,これ以降はローカルで行ったことと同じようなことを行うだけです.
はじめに,git pushでherokuにデプロイします.comments.csvやマイグレーションファイルを含めてデプロイしている点がポイント(?)です.
.vendordb/development.db等のローカルでしか必要のないファイルやディレクトリは適宜.gitignoreに追加してください.)

~/
$ git add .
$ git commit -m "deploy"
$ git push heroku master

マイグレーションファイルからテーブルを作成します.

~/
$ heroku run bundle exec rake db:migrate

heroku runの後にコマンドを続けることで,Heroku上でそのコマンドを実行するということですね.

csvファイルからレコードの追加

最後に作成したテーブルにcsvファイルからレコードを追加しましょう.
ローカルではSQLite3でしたが,HerokuではPostgreSQLなので,少し書き方を変える必要がありますが,やっていることは同じです.

~/
$ heroku pg:psql
...
(app name):DATABASE=> \copy comments (id, name, title, detail) from 'comments.csv' with csv
COPY 3

(app name):DATABASE=> select * from comments;
 id | name |   title   |     detail      
----+------+-----------+-----------------
  1 | hoge | hello     | Hello Sinatra
  2 | foo  | goodbye   | Goodbye Sinatra
  3 | bar  | emergency | Reply Required

(app name):DATABASE=> \q

レコードが追加されていますね.
最後に確認として,https://(app name).herokuapp.com/にアクセスしてみてください.
ローカルで確認した時と同じように表示されていれば成功です!

終わりに

ボリュームの割にタイトルと中身のミスマッチ感がありますが,Sinatraアプリの作成からローカルおよびHerokuにデータベースを構築し,csvファイルからレコードを追加するところまでを書きました.
今回,「はじめに」で述べたように読み取りだけを行うアプリをHeroku上にて動かしたく,試行錯誤の結果,本記事のように進めると解決できたということで,色々ナンセンスな部分はあると思います.(個人的に,ファイルのほとんどをデプロイするのは如何なものかと思っています.)
もっと良いやり方あるよ〜って方はコメントお願いします.
また,書いているうちにデータベースとテーブルがごっちゃになってきたので,気が向いたらまた直します.

参考サイト

Heroku Postgres | Heroku Dev Center
Heroku アプリに PostgreSQL を導入する - Corredor
SQLite3 のテーブルに CSV でデータを読み込む - CUBE SUGAR CONTAINER
CSVファイルをデータベースにインポートする - Qiita