CUBE SUGAR CONTAINER

技術系のこと書きます。

Python の DB-API (Database API) とは何か

Python でデータベース周りについて調べていると、ドキュメントの中にちょくちょく DB-API (2.0) という単語が出てくる。 果たしてこの DB-API とは何者なのか、というのが今回の主題。

結論から言ってしまうと、DB-API というのは Python でリレーショナルデータベースを操作するために定義された API の仕様を指している。 仕様というのがポイントで、これは具体的な実装を指しているわけではない。 DB-API は Python に関する仕様を決める PEP という枠組みの中で PEP0249 というドキュメントに書かれている。

PEP 249 -- Python Database API Specification v2.0 | Python.org

なぜ DB-API が必要になるのだろうか? それは、リレーショナルデータベースに SQLite3 や MySQL など、数々の実装がある点から説明できる。 それらは SQL を扱うリレーショナルデータベースという点で共通しているにも関わらず、それを操作するためのモジュールがそれぞれバラバラに API を決めていてはユーザにとって利便性が低くなる。 そこで登場するのが DB-API という仕様で、モジュールが仕様を満たすように作られることで、ユーザは使っているリレーショナルデータベースとモジュールに関わらず同じインターフェースを通して操作できるようになる

ここからは実際に色々なデータベース操作用モジュールで DB-API を触ってみることにする。 環境には Mac OS X を使っている。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11
BuildVersion:   15A284

SQLite3 (sqlite3)

まずは Python 組み込みの SQLite3 モジュールから。 組み込みなので Python さえ入っていれば特に何をするでもなく使うことができる。

Python の REPL を使って動作を確認していく。 インポートは sqlite3 という名前でできる。

$ python -q
>>> import sqlite3

例えば DB-API では apilevel や threadsafety といったグローバル変数が定義されている。

>>> sqlite3.apilevel
'2.0'
>>> sqlite3.threadsafety
1

また、データベースへの接続は connect() 関数を使うことになっている。

>>> sqlite3.connect
<built-in function connect>

たしかに DB-API 2.0 が実装されているようだ。

MySQL (PyMySQL)

次は MySQL 用のモジュールとして PyMySQL を試してみる。

Homebrew で MySQL をインストールした上で pymysql を入れる。

$ brew install mysql
$ pip install pymysql

モジュールは pymysql という名前でインポートできる。 次のように、やはり別のモジュールであっても DB-API の仕様に則って同じグローバル変数、同じ関数が実装されている。

$ python -q
>>> import pymysql
>>> pymysql.apilevel
'2.0'
>>> pymysql.threadsafety
1
>>> pymysql.connect
<function Connect at 0x100b44400>

Postgresql (psycopg2)

次は Postgresql 用のモジュール psycopg2 を試してみる。

Homebrew で Postgresql をインストールした上で、psycopg2 を入れる。

$ brew install postgresql
$ pip install psycopg2

モジュールは psycopg2 という名前でインポートできる。 やはり、このモジュールも DB-API に則って必要なグローバル変数と関数が用意されている。 しかし、先のふたつとは threadsafety の値が異なっていることもわかる。

$ python -q
>>> import psycopg2
>>> psycopg2.apilevel
'2.0'
>>> psycopg2.threadsafety
2
>>> psycopg2.connect
<function connect at 0x10bd6d598>

DB-API を使ってみる

次は実際に DB-API を使ってみることにする。 リレーショナルデータベースには MySQL を使う。

動作確認用のデータベースとテーブルを作って行をひとつ追加しておく。

$ mysql.server start 
$ mysql -u root

mysql> create database if not exists test;
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> use test;
Database changed

mysql> create table users (
    ->     id Integer auto_increment,
    ->     name Text not null,
    ->     primary key (id)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> insert into users (name) values ("foo");
Query OK, 1 row affected (0.00 sec)

mysql> quit
Bye

次に、Python の DB-API 経由で行を取得してみる。 connect() 関数でデータベースへの接続を確立したら、cursor() でカーソルオブジェクトを取得する。 カーソルオブジェクト経由で SQL を実行して結果を取得する。

$ python -q
>>> import pymysql
>>> connection = pymysql.connect('localhost', user='root', db='test')
>>> with connection.cursor() as cursor:
...     sql = 'select `id`, `name` from `users` where `name`=%s'
...     cursor.execute(sql, 'foo')
...     result = cursor.fetchone()
...     print(result)
...
1
(1, 'foo')

ばっちり。

使い終わったらコネクションを閉じる。

>>> connection.close()

まとめ

今回は Python の DB-API について調べてみた。 DB-API は Python でリレーショナルデータベースを扱うモジュールに求められる仕様のことで、巷のモジュールはそれを実装している。 とはいえ、実際に Python でリレーショナルデータベースを扱う際には、インピーダンスミスマッチの問題があるので SQL を直接発行するような低レベルの DB-API を直接使う場面というのは少ないと思う。 現実的には O/R マッパー (例えば SQLAlchemy など) が内部的に DB-API を使うことで、間接的にその恩恵に預かることになるだろう。