PandasとSQLAlchemyでPostgreSQL(Heroku Postgres)を読み書きする方法

この記事では、Heroku Postgresの導入から初期設定、PandasとSQLAlchemyモジュールでデータベースを読み書きする方法を紹介しています。

Heroku Postgresは無料プラン(正しくは、Hobby Devプラン)で1万レコードまで使用することができ、Webアプリケーションの簡単な開発テスト、スクレイピングのデータの保管場所にぴったりです。

MEMO
1つ上のHobby Basicプランにアップグレードすると、1000万レコードまで使用することができます。詳しくはこちら

さっそく導入方法を見てみましょう。

Heroku Postgresを導入する

STEP.1
Herokuにログインする
ターミナル
$ heroku login
STEP.2
Herokuの特定のアプリケーションにHeroku Postgresを追加する

Heroku Postgresを使用する場合、無料有料枠問わずクレジットカードの登録が必須です。先にアカウントページでクレジットカードを登録させましょう。

ターミナル
$ heroku addons:create heroku-postgresql:hobby-dev -a {{ アプリ名 }}

アプリ名が分からない場合は、heroku appsコマンドで、Herokuアカウントに登録されているアプリケーション一覧が表示されます。

ターミナル
$ heroku apps
出力
=== アカウント名 Apps
アプリケーション名1
アプリケーション名2
アプリケーション名3

アプリが無い場合、heroku createコマンドを使用して新規作成します。

作成方法にはアプリケーション名を指定する方法としない方法の2パターンがあります。指定しない場合、適当な名前が設定されます。

ターミナル
$ heroku create
$ heroku apps:create {{ 任意のアプリ名 }}
注意
アプリ名を指定した場合、他のアプリケーション(Heroku内の全てのアプリが対象)と名前が被っているとアプリケーションを作成できません。
STEP.3
データベースの状態を確認する
ターミナル
$ heroku pg:info -a {{ アプリ名 }}

Heroku Postgresの初期設定

この章では、PandasからHeroku Postgresを操作するためのセットアップを行います。

ターミナルから接続する

ターミナル
$ heroku pg:psql -a {{ アプリ名 }}

psqlコマンドが使えない人は、Homebrewを使ってPostgreSQLをローカルPCにインストールします。

ターミナル
$ brew install postgresql
ターミナル
>>>$ psql -V
psql (PostgreSQL) 11.2

タイムゾーンを変更する

STEP.1
デフォルトのタイムゾーンを確認する

Herokuのサーバー上のタイムゾーンは、デフォルトでUTC時間になっているので、日本時間(Asia/Tokyo)に変更します。

psql|現在時刻を確認する
select current_timestamp;
出力
       current_timestamp       
-------------------------------
 2020-01-15 06:49:43.847471+00
(1 row)
STEP.2
タイムゾーンをAsia/Tokyoに変更する
psql|DBのタイムゾーンを変更する
alter database {{ データベース名 }} set timezone = 'Asia/Tokyo';

以下のコマンドで、アプリケーションに紐付いているデータベースのURLを得ることができます。

ターミナル
$ heroku config --app {{ アプリ名 }}
出力
DATABASE_URL: postgres://{{ User }}:{{ Password }}@{{ Host }}:{{ Port }}/{{ Database }}
STEP.3
確認する

ちゃんと変更されたか、もう一度確認します。

\qコマンドで一度DBからログアウトして、heroku pg:psqlコマンドで接続し直します。

psql|現在時刻を確認する
select current_timestamp;
出力
       current_timestamp       
-------------------------------
 2020-01-15 15:52:56.008592+09
(1 row)

ちゃんと9時間後に設定されましたね!

テーブルを作成する

STEP.1
テーブルを作成する
  • 1列目:id(integer) PRIMARY KEY
  • 2列目:name(varchar(100))
psql
create table {{ 任意のテーブル名 }}(
id integer not null,
name varchar(100) not null,
primary key (id)
);

メタコマンド\dでテーブル情報を確認してみましょう

psql
\d {{ テーブル名 }}
出力
                    Table "public.{{ テーブル名 }}"
 Column |          Type          | Collation | Nullable | Default 
--------+------------------------+-----------+----------+---------
 id     | integer                |           | not null | 
 name   | character varying(100) |           | not null | 
Indexes:
    "{{ テーブル名 }}_pkey" PRIMARY KEY, btree (id)
STEP.2
テーブルにデータを追加する
psql
insert into {{ テーブル名 }} (id, name) values (1, 'tanu');
insert into {{ テーブル名 }} (id, name) values (2, 'hack');
STEP.3
確認する
psql
select * from sample;
出力
 id | name 
----+------
  1 | tanu
  2 | hack
(2 rows)

おまけ:DB関連のコマンドまとめ

・カラムの型変換 : ALTER TABLE テーブル名 ALTER COLUMN カラム名 TYPE データ型
・データベース情報の確認 : \d
・テーブル情報の確認 : \d {{ テーブル名 }}

PandasでPostgreSQLを操作する

個人的に、PythonからPostgreSQLのレコードを読み込んだり追加したりする方法で、もっとも簡単かつラクな方法を紹介します。

それはpandas.DataFramesql系のメソッドを使う方法です。

  • read_sql() : データベースのテーブルをpandas.DataFrameとして読み込む
  • to_sql() : pandas.DataFrameをデータベースのテーブルに追加する

それぞれ順に紹介します。

DataFrameとしてテーブルを読み込む

pandas.DataFrameread_sqlメソッドを使用すると、PostgreSQLのテーブルをDataFrameとして変数に格納することが出来ます。

Python
df = pd.read_sql(sql, con)

必須パラメータは、以下の2つです。

  • sqlパラメータ : SQLクエリまたはテーブル名
  • conパラメータ : SQLAlchemyのサーバー接続エンジンまたはデータベースURL

もっと詳しくパラメータのことを知りたい方は、Pandasの公式ページからどうぞ

データベースとDataFrameを繋げるためにO/RマッパーであるSQLAlchemyモジュールをインストールします。

ターミナル
pip install sqlalchemy

それでは、pandas.DataFrameread_sqlメソッドとsqlalchemyクラスのcreate_engineメソッドを使用して、PostgreSQLからテーブルをDataFrameに格納するサンプルプログラムを見てみましょう。

Python
import pandas as pd
from sqlalchemy import create_engine

# PostgreSQLのサーバー接続エンジンを作成する
engine = create_engine('{{ データベースのURL }}')

# 変数dfにPostgreSQLの指定したテーブルの全レコードをpandas.DataFrameとして格納する
df = pd.read_sql(sql='SELECT * FROM {{ テーブル名 }};', con=engine)
print(df)
出力
   id  name
0   1  tanu
1   2  hack

以下のコマンドでHerokuのアプリケーションに紐付けられているデータベースのURLを得ることが出来ます。

ターミナル
$ heroku config --app {{ アプリ名 }}
出力
DATABASE_URL: postgres://{{ User }}:{{ Password }}@{{ Host }}:{{ Port }}/{{ Database }}

テーブルにDataFrameを追加する

pandas.DataFrameto_sqlメソッドを使用すると、PostgreSQLに存在するテーブルにDataFrameを追加することが出来ます

Python
pandas.DataFrame.to_sql(name, con, if_exists='append', index)

設定するパラメータは、以下の4つです。

  • nameパラメータ : 追加先のテーブル名
  • conパラメータ : SQLAlchemyのサーバー接続エンジン
  • if_existsパラメータ : 値をappendにするとテーブルに追加できます
  • indexパラメータ : DataFrameのインデックスをテーブルに追加するかどうか

もっと詳しくパラメータのことを知りたい方は、Pandasの公式ページからどうぞ

pandas.DataFrameto_sqlメソッドについて補足すると、デフォルトのデータベースはSQLiteなので、必ずconパラメータにはデータベースのURLではなく、SQLAlchemyのサーバー接続エンジンを指定しなければいけません。

Python
import pandas as pd
from sqlalchemy import create_engine

# PostgreSQLのサーバー接続エンジンを作成する
engine = create_engine('{{ データベースのURL }}')

# 適当なDataFrameを作成する(ただし、カラム名は必ずデータベースのものと同じにする)
df = pd.DataFrame(data=[[3, 'たぬ'], [4, 'ハック']], columns=['id', 'name'])
#    id  name
# 0   3  たぬ
# 1   4  ハック

# PostgreSQLの指定したテーブルにpandas.DataFrameを追加する
df.to_sql('{{ テーブル名 }}', con=engine, if_exists='append', index=False)

ターミナルに戻って、ちゃんとデータベースに追加されているか確認しましょう。

psql
select * from sample;
出力
 id | name  
----+-------
  1 | tanu
  2 | hack
  3 | たぬ
  4 | ハック
(4 rows)

きちんと追加されていましたね。

nameパラメータのテーブル名を間違えてしまうと、PostgreSQLに新しくテーブルが作成されてしまうので注意しましょう。