PythonでTUIアプリを作ろう 第二部入魂編 ( その7 Pythonのデータアクセス機能 )
ListDBPのデータベースにMySQLを使用してみる。
今回は、まず「Pythonとデータベース」について説明したうえで、「ListDBPにおける複数データベース対応」について記述します。
[1] PythonによるDBアクセス
データを格納するには、何らかのデータベースが必要ですが、RDB(Relational Database)を使用するのが一般的でしょう。
Pythonでは、DB-APIと呼ばれるものが標準ライブラリとして含まれており、SQLite3というシングルユーザー用データベースが使用可能です。
上記のサイトによると、
SQLite は、軽量なディスク上のデータベースを提供する C ライブラリです。別のサーバプロセスを用意する必要なく、 SQL クエリー言語の非標準的な一種を使用してデータベースにアクセスできます。一部のアプリケーションは内部データ保存に SQLite を使えます。また、SQLite を使ってアプリケーションのプロトタイプを作り、その後そのコードを PostgreSQL や Oracle のような大規模データベースに移植するということも可能です。
とのこと。
なにゆえ、移植が可能かといえば、Pythonによるデータベースアクセスには、事前に決められたインターフェース"PEP 249"があり、ほとんどのモジュールは、この標準にしたがって設計されているからです。
PEPに従ったDB-APIでは、importするモジュール以外は、同じような構造になります。SQLite3以外のデータベースも、上記PEPに従ったインタフェースモジュールが提供されていますので、スクリプトは下記のような構造になるでしょう。
# Importするモジュールは、それぞれのDB用
# これは標準DBのSQLite3用
import sqlite3
# MySQLの場合
import pymysql
# Postgresの場合
import psycopg2
ここからは、同じようなコードとなるはずです。Sqlite3であれば、こんな感じです。
con = sqlite3.connect('example.db')
cur = con.cursor()
cur.execute('''CREATE TABLE stocks
(date text, trans text, symbol text, qty real, price real)''')
cur.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
con.commit()
con.close()
ここでは、DB-APIや各データベースモジュールの詳細は説明はしません。様々なサイトがありますから、そちらを参照してください。
DB-APIの限界
個人用のデータ管理ならSQLite3が最適ですが、あくまでシングルユーザー用ですから、Webアプリのように、マルチユーザーからアクセスする場合には、MySQLやPostgressなどを使うのが一般的です。
理想を言えば、「1つのスクリプトで複数データベースをサポートしたい」わけですが、現実的には無理があります。DB-APIの基本的な流れは、先に述べたように標準化されていますが、あくまで関数が共通化されただけで、SQLレベルまで合わされているわけではないからです。
例えば、Prepareステートメントで使われるバインド変数は、SQLite3では’?’で表しますが、他のDBでは’%s’だったりします。また、標準SQL以外の関数は、それぞれのRDBの仕様に依存してしまいます。
このようにRDBによる違いが少なくないので、「共通性の追求には限界がある」わけなのです。
[2] ListDBPをMySQL上で動かす。
そこで、今回のアプリケーションListDBPでは、「データベースによって異なる部分を抜き出し、個別対応する」という泥臭い施策になっています。
以下、簡単に説明しておきます。
(1) データベースの選択
実行時にパラメータとして、データベース名と接続文字列を取得します。
if __name__=="__main__":
if len(sys.argv) == 3:
database_name = sys.argv[1]
database_connect = sys.argv[2]
else:
database_name = 'SQLITE3'
database_connect = './db/ListDB.sqlite3'
#database_name = 'MYSQL'
#database_connect = 'mysql://user01:pass01@localhost/ListDB'
main(database_name, database_connect)
(2) データベースモジュールでの処理
ListDBInterfaceクラスで、対応するデータベースモジュールをImport、それぞれの処理を委譲していくという仕組みで、複数データベースに対応します。
SQLite3対応はdbsqlite3.pyに、MySQL用はdbmysql.pyに記述してあります。今回はスクリプトに組み入れていませんが、Postgress用のものをdbpostgres.pyとして残しておきました。
class ListDBInterface:
def __init__(self, database_name, database_connect):
if (database_name == "SQLITE3"):
from . import dbsqlite3
self.db = dbsqlite3.ListDBSQLite3(database_connect)
elif (database_name == "MYSQL"):
from . import dbmysql
self.db = dbmysql.ListDBMySQL(database_connect)
self.connection = self.db.connection
self.cursor = self.db.cursor
# --------------------------------------------------------
def get_drop_meta_sql(self):
return self.db.get_drop_meta_sql()
def get_define_sql(self):
return self.db.get_define_sql()
def get_insert_meta_sql(self):
return self.db.get_insert_meta_sql()
def get_update_meta_sql(self, dbName):
:
:
[3] MySQL環境の整備
アプリケーションを実行するには、まずMySQL環境をセットアップする必要があります。以下、Ubuntu環境での実行について記述します。
(1) MySQLのインストール
何はともあれ、MySQL本体をインストールしないと始まりません。
$ sudo apt-get install mysql-server
(2) セットアップ
MySQL環境をセットアップしていきます。
1. rootのパスワードを設定
$ sudo mysql -u root
mysql> alter user 'root'@'localhost' identified by 'xxxx' ;
mysql> flush privileges;
2. ListDBデータベースの作成
mysql> create database ListDB;
3. ユーザーの作成
作成したListDBデータベースについて、全権限を持つユーザーを作成します。ここでは、ユーザー「user01」をパスワード「pass01」で設定しました。
mysql> create user 'user01'@'%' identified by 'pass01';
mysql> create user 'user01'@'localhost' identified by 'pass01';
mysql> grant all privileges on ListDB.* to user01@'%';
mysql> grant all privileges on ListDB.* to 'user01'@'localhost';
4. 外部接続の許可
外部からの接続を許可するには、/etc/mysql/mysql.conf.d/mysqld.cnfに設定が必要なようです。
bind-address = 127.0.0.1
↓
bind-address = (接続したいマシンのIPアドレス)
#どのIPからも接続許可する場合は、bind-address行をコメントアウト。
5. Python MySQLモジュールのインストール
いくつかのMySQL対応モジュールがあるようですが、ここではPyMySQLを使用していますので、pipでインストールします。
$ pip3 install PyMySQL
[5] スクリプトの変更と実行
環境が整備されたら、まずはMySQLにデータをロードします。その後、スクリプトを実行、内容を確認していきます。
(1) データロード
load_db.shスクリプトを下記のように書き換えて実行します。
python3 load_db.py 'MYSQL' 'mysql://user01:pass01@localhost/ListDB' './csv'
#python3 load_db.py 'SQLITE3' './db/ListDB.sqlite3' './csv'
(2) スクリプトの実行
list.pyの起動部を下記のように書き換えて実行します。
if __name__=="__main__":
if len(sys.argv) == 3:
database_name = sys.argv[1]
database_connect = sys.argv[2]
else:
#database_name = 'SQLITE3'
#atabase_connect = './db/ListDB.sqlite3'
database_name = 'MYSQL'
database_connect = 'mysql://user01:pass01@localhost/ListDB'
main(database_name, database_connect)
以上となります。
なお、今回説明したMySQL対応は、あくまで「Pythonおける複数データベース対応」のテスト用に作成したものですから、一つの参考としてご理解ください。