pythonでDB操作
DBMSに依存しないDB操作プログラミング
SQLiteやMySQL, PostgreSQL, Oracle Databaseなど色々DBMSがあり、それぞれプログラムから使用する際はDBMS毎に異なったコーディングが必要。覚えきれない…。
そこで、オブジェクト指向マッピング(ORM)という技法を使えば、DBMS毎にコードを変える必要が無くなるのです!
pythonにはSQLAlchemy
というパッケージがあり、それで実現できます。やったね!
ということで、これは是非身につけたい。
まずはSQLite用のコーディング
SQLite用に書くとこんな感じなります。
sqlite3
はpythonに標準でインストールされていて、軽くて使いやすい。らしい。
import sqlite3 # DBのコネクションを生成 # :memory:を指定すると、DBがメモリ上に保存され、接続が終了すると消える。DBファイルに出力するときはファイル名を指定する。 conn = sqlite3.connect(':memory:') # DBを操作するためのカーソルを生成 curs = conn.cursor() # executeでSQLを実行 curs.execute( 'CREATE TABLE persons(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING)') curs.execute( 'INSERT INTO persons(name) values("Mike")' ) curs.execute( 'INSERT INTO persons(name) values("Nancy")' ) conn.commit() curs.execute('SELECT * FROM persons') # クエリ結果をコンソールに表示 print(curs.fetchall()) conn.close()
コンソールの表示結果は↓
[(1, 'Mike'), (2, 'Nancy')]
これがMySQLやPostgreSQLになるとまた別のパッケージをインストールして、そのパッケージの使い方に合わせたコーディングが必要。
SQLAlchemyを使ってみる
SQLAlchemy
を使ってSQLiteのDBを操作するコーディングはこうなる。
import sqlalchemy import sqlalchemy.ext.declarative import sqlalchemy.orm # データベース接続のためのEngineオブジェクトを取得 engine = sqlalchemy.create_engine('sqlite:///:memory:') # Baseにオブジェクトを生成 Base = sqlalchemy.ext.declarative.declarative_base() # Baseに入ったオブジェクトのクラスを継承して、テーブルの定義クラスを作成 class Person(Base): __tablename__ = 'perons' # テーブルのカラムを定義 id = sqlalchemy.Column( sqlalchemy.Integer, primary_key=True, autoincrement=True) name = sqlalchemy.Column(sqlalchemy.String(14)) # 使用するデータベースのEngineをBaseに設定 Base.metadata.create_all(engine) # DBにアクセスするため、Engineのセッションを作成 Session = sqlalchemy.orm.sessionmaker(bind=engine) session = Session() # DBに追加するオブジェクトを作成し、セッションに追加 person1 = Person(name='Mike') person2 = Person(name='Nancy') session.add(person1) session.add(person2) session.commit() # クエリの結果全てをリストで取得 persons = session.query(Person).all() # リストの中身をコンソールに表示 for person in persons: print(person.id, person.name)
出力結果は↓
1 Mike 2 Nancy
UPDATE
する時は次のように記述
# データを更新 # まずは更新対象のデータの1行目を取得 person3 = session.query(Person).filter_by(name='Mike').first() # データを書き換え、セッションに追加 person3.name = 'Michel' session.add(person3) session.commit()
ここで色々と疑問
ひとまず、使い方の流れを理解した。…が、ちょっといくつか分からないコードがある。 基礎的な話です。
分からないポイント
pythonのクラスとかその辺がまだ身に付いていない感じ。
1つ目
import sqlalchemy engine = sqlalchemy.create_engine('sqlite:///:memory:')
sqlalchemy
をimportしてるんで、sqlalchemy
ていうモジュールがあって、その中でcreate_engine
関数が定義されてるんですよね?
探してみた結果。
sqlalchemy
パッケージ(フォルダ)があり、その配下のengine
パッケージの__init__.py
の中に定義されていました。
つまり、普通に呼ぶときは
import sqlalchemy.engine engine = sqlalchemy.engine.create_engine('sqlite:///:memory:')
となるはず?これで実行しても同じ結果でした。
ここで気付いたのが、「あれ、importする時って、パッケージ名.モジュール名
だよね?」
あー、__init__.py
だとモジュール名が省略されるということか。
なので、import sqlalchemy
の一文で、sqlalchemy
配下の__init__.py
が呼ばれる。
そこには
from .engine import create_engine, engine_from_config
と記述がある。
ここでengine
が出てきました。でもこれはパッケージなので、またモジュール名が無い。
そして__init__.py
の中にcreate_engine
がいる。
つまり、import sqlalchemy
だけでその配下のengine
パッケージの__init__.py
まで読み込まれるってことかー…。慣れないorz
はい、次
2つ目
Base = sqlalchemy.ext.declarative.declarative_base() … Base.metadata.create_all(engine)
この流れ…。
あるクラスのオブジェクトを生成して、そのクラスのインスタンスメソッドcreate_all
が呼ばれてるんだろうか…でも間の.metadata.
は何者か。
Base.metadata
はBase
に入れたクラスオブジェクトで、そのインスタンス変数matadata
にまたクラスのオブジェクトが入ってて
そのクラスがcreate_all
を定義しているってことかな。
sqlalchemy
のドキュメントで調べると、そのクラスはclass sqlalchemy.schema.MetaData
ということになっているが、いない。
pythonista3アプリだと宣言箇所に飛べないから
探すのが大変…。(修行にはなりそう)
そして、sqlalchemy.sql.schema
にいたーーー!
実態はsqlalchemy.sql.schema.MetaData
でした。
なんで違うかと考えたところ、ドキュメントには「MetaData API」という文字があったんで、これはあくまでAPIとしてのマニュアルってことか。
ドキュメント上は、実態とか関係なしにモジュールの呼び出し方だけ記載してあるってことなんですねー。知らなかった。
ドキュメント(英語)ちゃんと読めって話ですが、今はまだ時間がかかりすぎると思うので、もうちょいスキル上がってから読みます! 一旦モヤモヤは落ち着いたので良しとしよう。
最後
Session = sqlalchemy.orm.sessionmaker(bind=engine) session = Session()
Session
とsession
の違いとは…。
Session
はクラスのオブジェクトが入ってるとして、session
はそれをインスタンス化したものだよねきっと。
ドキュメントに
A configurable Session factory.
The sessionmaker factory generates new Session objects when called, creating them given the configurational arguments established here.
て書いてある。訳すと
設定可能なセッションファクトリ。
sessionmakerファクトリは呼び出されると新しいSessionオブジェクトを生成し、ここで設定された構成引数を与えられてそれらを作成します。
うん、それっぽい!(疲れてきた)
まとめ
なるほど、こうやってDBMSの種類に合わせたクラスの構造自体を作っていって、操作する時はその出来上がったクラスをインスタンス化して使うってことなんだなぁ。きっと。
今はそう理解しておこう…。またスキルが上がった時にさらに深掘りしたいと思います
詳しい人いたら是非指摘して欲しい!