アーカイブ

Posts Tagged ‘Python’

PythonSQLMapperのMySQLdbへの依存を除去しました

PythonSQLMapperのMySQLdbへの依存を除去しました。
https://github.com/marvelph/PythonSQLMapper

現状、下記の三種類のデータベースモジュールをサポートしています。

また、データベースモジュールには適切に実装されたラッパーも利用できます。
PythonSQLMapperでコネクションプルールが必要な場合は、sqlalchemy.poolの利用を推奨します。
下記のようなコードで、コネクションプルールが有効になります。

import sqlalchemy.pool as pool
import MySQLdb

MySQLdb = pool.manage(MySQLdb)

mapper = SQLMapper.Mapper(driver=MySQLdb, host='127.0.0.1', db='db', user='user', passwd='passwd', charset='utf8')
カテゴリー:開発 タグ: , , ,

PythonSQLMapperを公開しました

iBATIS風味のSQLマッピング、PythonSQLMapperをリリースしました。
https://github.com/marvelph/PythonSQLMapper

現在メインのお仕事ではバックエンド「も」担当しているのですが、データベースのアクセスにSQLAlchemyを使っています。
迷った末の選定でしたが、正直失敗したと思っています。

例えばアトミックな更新を行いたいとすると、1エンティティをロック付きでフェッチして、値を変更してコミットします。
一定条件の複数レコードを同時更新したい場合は、複数エンティティをロック付きでフェッチして、ループで値を変更してコミットです。

生成したばかりのエンティティのオートインクリメント・プライムキーを他のエンティティに保存しようとすると、一旦コミットするか再フェッチしないと値が手に入りません。

複雑なクエリ条件でエンティティを引っ張ろうとすると、フィルタをメソッドのチェーンで表現する必要があるのですが、どうみてもSQLよりも難解になります。
その上、pythonのインタープリタでしか試せません。

バッチ処理などでループ中で利用すると、マネージドエンティティが激増して、猛烈に重くなったりします。

これらはO/Rマッピング全般の傾向ではあると思うんですが、コードが複雑になるだけで良い事が無いというのが実感です。
みんな、そんなにSQLが嫌いなのかと。

iBATISのようなツールも探しまわったのですが、良い物は見つかりませんでした。
そこでCocoaSQLMapperのpythonへの移植を考えていたのですが、新サービス立ち上げのチャンスが回って来たので、土日をつぶして「ガッ」と実装しました。
その後実際に新サービスの開発に使っているのですが、良い感じに使えているので公開する事にしました。

移植と言っても、重要な部分に差異があります。
まずCocoaSQLMapperはSQLite専用であるため、テーブルのフィールド側には「型」がありません。一方、Objective-Cのクラスのプロパティには「型」があります。
そのため、オブジェクトをパラメータとし渡す場合も、クラスを結果セットの入れ物として指定する場合も、Objective-C側の型情報を使ってSQLiteにアクセスする仕組みでした。

一方PythonSQLMapperですが、現状ターゲットにしているMySQLはテーブルのフィールドに型を持ちます。
またpythonのクラスの属性の型は変化できますし、結果セットの受け取り時などに生成前であれば、属性の名前も判りません。
そのため、型情報はデータベース側を当てにする事にしました。
この考え方はパラメータとしてオブジェクトを渡す場合は問題が無いのですが、リザルトタイプにクラスを指定して結果セットを受け取る場合が問題になります。
そこで、「結果セットとクラスのコンストラクタが整合すれば良い」という事にしました。

これは次のようなSQLの結果を、

SELECT name, age FROM person;

こんなクラスであれば受け取れるという事です。

class Person(object):
    def __init__(self, name, age):
        ...

いずれにしても、データベーススキーマを解析したりして、マッピングルールを何らかの方法で与えるという事はしないという考え方は変えていません。
エンティティに対する細工も不要で、特別な親クラスなども導入していません。
マネージドエンティティという考え方を、避けているという事です。

また使い勝手を考えて、パラメータにはdictも利用可能としました。
さらにリザルトタイプは省略が可能で、匿名クラスのオブジェクトに結果セットを属性として取り付けて返します。

dictと匿名クラスだけで使う事も可能という事になりますが、エンティティクラスにはメソッドが使えますから、多くのユースケースでは従来の使い方が有効だと思います。

当面の開発目標としては、次のような事を考えています。

  • PostgreSQLのサポート
  • コネクションプールの実装
  • Flask Extensionの提供

とりあえず、コネクションプールが最優先です。

カテゴリー:開発 タグ: , ,

ScanSnap付属のCardMinderに読み込んだ名刺をEvernoteに流し込む

2011年1月23日 3件のコメント

別にCardMinderが使い難いという事は無いのですが、今時モバイルで参照できないのはどうかと思ったので、Evernoteに流し込んで名刺用のタグでも打つ事にしました。
~/Documentsにファイルを作っている気配が無いので、~/Library/を探してみた所~/Library/Application Support/CardMinder/CardMinder DB.cmdbを見つけました。
これはバンドルで、中にCardMinder.sqldbとImagesディレクトリがあり、Imagesディレクトリの中には名刺のPDFファイルが沢山入っています。
PDFは1ファイルで裏表を収容しているのですが、ファイル名がUUIDあり、このままだと検索が面倒です。
そこでCardMinder.sqldbの方をsqlite3で開いてみた所、中に氏名などのデータが収まっていました。
とりあえず、スクリプトで氏名を抜いてPDFのファイル名にしてやり、最後に手動でEvernoteに流し込む事にしました。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import datetime
import shutil
import sqlite3

source_directory = os.environ["HOME"] + "/Library/Application Support/CardMinder/CardMinder DB.cmdb/Images/"
destination_directory = os.environ["HOME"] + "/Desktop/CardMinderToEvernote/"

connection = sqlite3.connect(os.environ["HOME"] + "/Library/Application Support/CardMinder/CardMinder DB.cmdb/CardMinder.sqldb")
cursor = connection.execute("select ZFULL_NAME, ZFACE_IMAGE_FILE, ZREGISTER_DATE from ZCARD")
for row in cursor:
	print row[0], row[1], row[2]
	shutil.copyfile(source_directory + row[1], destination_directory + row[0] + ".pdf")
connection.close

デスクトップには、あらかじめCardMinderという名前でフォルダを作っておきます。
スクリプトが完了すると、氏名がファイル名になったPDFがフォルダ内にコピーされるので、これをEvernoteにドロップすれば完了です。

後日、差分追加する時がくると思います。
その時は登録日時で抽出するようにSQLを書き換える必要があるんですが、タイムスタンプらしい日付のデータフォーマットに見当が付かなくて、保留しています。
小数値なので、てっきりunixtimeだと思ったんですが……

カテゴリー:デスクトップ, 開発 タグ: