ぽよメモ

レガシーシステム考古学専攻

SQLAlchemyを使ってみる

だいぶ前に学務課のwebページをスクレイピングしてデータを取り出す記事をかきました。これをいちいち通知していたら毎回何百という通知を見ることになってしまいます。
そこで、SQLiteを用いてこれらの情報をデータベースで管理することで、新規で追加されたもののみを通知するようにしていきたいと思います。
が、いかんせんSQLiteなんてさっぱりわかりません。SQL構文とかポカーンですよポカーン。
そこで、O/Rマッパー(以下ORM)というものを用います。こいつはデータベースとPythonの橋渡し役、コミュニケーションを取ってくれるやつです。
これを用いるメリットとしては「Pythonのプログラム内からSQL構文を排除できる」ということ(でいいのかな
様々なORMがあり、また自作する人も多くいますが、いまのところ最も用いられていそうなSQLAlchemyを使っていこうと思います。対応するデータベースが豊富で、かつ日本語での解説サイトも結構あるのでこいつが良いのかなと。

まずはモジュールのインストールから。

pip install sqlalchemy

完了です。
SQLAlchemyでは、Tableクラスとユーザ定義のマッピング対象クラスを作成し、mapper()を用いてこれらをマッピングするのが従来のやり方のようですが、正直難しいのでdeclalativeを用いて簡単な設定を書きます。もちろん従来のやり方にもメリットは有り、ORMとしての要素を可能なかぎり分離することができます(つまりdeclalativeではごっちゃになる可能性がある

from sqlalchemy import create engine, Column, Integer, String
from sqlalchemy.ext.declalative import declalative_base
from sqlalchemy.orm import sessionmaker

#DBへの接続(echoはログ機能の有無)
engine = create_engine('sqlite:///:memory:', echo=True)
Base = declalative_base()

class Qkou(Base):
    __tablename__ = 'kyukou'
    id = Column(Interger, primary_key=True) 
    subject = Column(String) #教科名
    teacher = Column(String) #担当教員
    week = Column(String) #曜日
    period = Column(String) #時限
    abstract = Column(String) #概要
    _property = Column(String) #詳細
    update = Column(String) #更新日

    def __init__(self, subject, teacher, week, period, abstract, _property, update):
        self.subject = subject
        self.teacher = teacher
        self.week = week
        self.period = period
        self.abstract = abstract
        self._property = _property
        self.update = update

    def __repr__(self):
        return "<Qkou('%s','%s','%s','%s','%s','%s','%s','%s'>" % (self.subject, self.teacher, self.week, self.period, self.abstract, self._property,  self.update)

#Tableの作成
Base.metadata.create_all(engine)

あとはDBに対して値を渡します。このとき「セッション」を作成します。こいつは、ORMがDBを操作する「ハンドル」に当たり、こいつを頻繁に使うことになります。

#Sessionにengineを結びつける
Session = sessionmaker(bind=engine)
session = Session()

この時コネクションはまだ開いていません。コネクションは、セッションを初めて利用した際にengineの管理するコネクションプールから取り出されて、「変更をすべてcommitする」 or 「セッションを閉じるまで保持される」(らしい。詳しくは分かんない
なにはともあれオブジェクトを新規追加します。

addinfo = Qkou('Twitter学概論', 'Pudding', '日曜日', '7限', '休講', '遊びに行くのでお休み', '9/16')
session.add(addinfo)

このときこれはまだ処理待ち(pending)状態で、DBには保存されていません。これを保存する必要ができた時点でフラッシュ(flush)され、SQLが実行されるらしいです。
変更を保存するには

session.commit()

また、クエリを実行してDBから目的のものだけを取り出すこともできます。

#条件に一致するものの中で最初のレコードを抽出。
#すべて取り出すには.first()ではなく.all()を用いる。
existinfo = session.quwry(Qkou).filter_by(teacher='Pudding').first()

#AND検索
from sqlalchemy import and_
filter(and_(Qkou.subject == '睡眠学', Qkou.teacher == 'Pudding'))
#こういう書き方も可
filter(Qkou.subject == '睡眠学').filter(Qkou.teacher == 'Pudding')
#OR検索
from sqlalchemy import or_
filter(or_(Qkou.subject == '睡眠学', Qkou.subject == 'ビール入門'))

これを用いて既存のレコードに同一のものがあるかどうか判定して、新規情報だけを入れることができる…?かな…?
まだまだ全然わかってないことが多すぎてなんかもう…(トランザクションって何?アイデンティティマップ?リレーション?ん???)