PythonでシンプルなORMライブラリ、datasetを使ってみた
サーモン大好き横山です。
今日はPythonでORM使いたいけど、雑に辞書型にマッピングしてくれるだけで良いというときに便利な dataset について紹介します。
install方法
venv環境にinstallして使って行きます。
python3 -m venv venv . venv/bin/activate
pip で datasetをinstallします。
pip install dataset
今回はMySQLにつなぐために、 mysqlclient
もinstallします。
pip install mysqlclient
mysqldへデータ投入
Other MySQL Documentation の world database
を利用して確認します。
curl -O https://downloads.mysql.com/docs/world.sql.zip unzip world.sql.zip
今回はlocalにmysqldをたてて、そこにデータを入れます。
mysql -uroot -p < world.sql
使用してみる
最初に local に接続するコードを書きます。(ここではload.pyに保存)
dataset.connect
の引数は SQLAlchemyのEngine URLで接続します。 dialect[+driver]://user:password@host/dbname[?key=value..]
詳しくは、SQL Alchemyのcreate_engine のドキュメントを参照してください。
#!/usr/bin/env python import dataset db = dataset.connect('mysql://root:password@localhost/world')
こちらのコードを実行後、対話モードを起動して操作していきます。
python -i load.py
cityのcountを取得
db['city']
で cityのテーブルの参照を取得します。
その後 count
関数を実行すると、cityテーブルの行数を取得できます。
>>> city = db['city'] >>> city.count() 4079
cityのテーブルからいろいろ取得してみた
find
関数を使い、cityデータを取得します。 _limit
の引数で10件と指定して取得します。指定しない場合は、全件(4079件)取得できます。
>>> for c in city.find(_limit=10): ... print(c) ... OrderedDict([('ID', 1), ('Name', 'Kabul'), ('CountryCode', 'AFG'), ('District', 'Kabol'), ('Population', 1780000)]) OrderedDict([('ID', 2), ('Name', 'Qandahar'), ('CountryCode', 'AFG'), ('District', 'Qandahar'), ('Population', 237500)]) OrderedDict([('ID', 3), ('Name', 'Herat'), ('CountryCode', 'AFG'), ('District', 'Herat'), ('Population', 186800)]) OrderedDict([('ID', 4), ('Name', 'Mazar-e-Sharif'), ('CountryCode', 'AFG'), ('District', 'Balkh'), ('Population', 127800)]) OrderedDict([('ID', 5), ('Name', 'Amsterdam'), ('CountryCode', 'NLD'), ('District', 'Noord-Holland'), ('Population', 731200)]) OrderedDict([('ID', 6), ('Name', 'Rotterdam'), ('CountryCode', 'NLD'), ('District', 'Zuid-Holland'), ('Population', 593321)]) OrderedDict([('ID', 7), ('Name', 'Haag'), ('CountryCode', 'NLD'), ('District', 'Zuid-Holland'), ('Population', 440900)]) OrderedDict([('ID', 8), ('Name', 'Utrecht'), ('CountryCode', 'NLD'), ('District', 'Utrecht'), ('Population', 234323)]) OrderedDict([('ID', 9), ('Name', 'Eindhoven'), ('CountryCode', 'NLD'), ('District', 'Noord-Brabant'), ('Population', 201843)]) OrderedDict([('ID', 10), ('Name', 'Tilburg'), ('CountryCode', 'NLD'), ('District', 'Noord-Brabant'), ('Population', 193238)])
特定の値を取得するときは、 find_one
を使います。取得した値は、OrderedDict
で取得できるので、カラム名をキーに値を取得できます。
>>> tokyo = city.find_one(Name='Tokyo') >>> print(tokyo) OrderedDict([('ID', 1532), ('Name', 'Tokyo'), ('CountryCode', 'JPN'), ('District', 'Tokyo-to'), ('Population', 7980230)])
複数候補がある場合は、1件目を取得します。
>>> for ja in city.find(CountryCode='JPN', _limit=5): ... print(ja) ... OrderedDict([('ID', 1532), ('Name', 'Tokyo'), ('CountryCode', 'JPN'), ('District', 'Tokyo-to'), ('Population', 7980230)]) OrderedDict([('ID', 1533), ('Name', 'Jokohama [Yokohama]'), ('CountryCode', 'JPN'), ('District', 'Kanagawa'), ('Population', 3339594)]) OrderedDict([('ID', 1534), ('Name', 'Osaka'), ('CountryCode', 'JPN'), ('District', 'Osaka'), ('Population', 2595674)]) OrderedDict([('ID', 1535), ('Name', 'Nagoya'), ('CountryCode', 'JPN'), ('District', 'Aichi'), ('Population', 2154376)]) OrderedDict([('ID', 1536), ('Name', 'Sapporo'), ('CountryCode', 'JPN'), ('District', 'Hokkaido'), ('Population', 1790886)]) >>> jpn = city.find_one(CountryCode='JPN') >>> print(jpn) OrderedDict([('ID', 1532), ('Name', 'Tokyo'), ('CountryCode', 'JPN'), ('District', 'Tokyo-to'), ('Population', 7980230)])
各カラムにアクセスするには、辞書のキーにカラム名を指定すれば、値が取れます。
>>> print(tokyo['ID']) 1532 >>> print(tokyo['Name']) Tokyo >>> print(tokyo['Population']) 7980230
カラム名一覧は、テーブル参照の columns
から取れます。これを利用して、レコードの値を一気に出力したりもできます。
>>> for col_name in city.columns: ... print(f'{col_name:>12}:{tokyo[col_name]}') ... ID:1532 Name:Tokyo CountryCode:JPN District:Tokyo-to Population:7980230
トランザクション・更新・挿入・削除
databaseの参照に begin
commit
rollback
の関数があります。
まず、トランザクションをはり、Tokyoの人口を更新したあとにrollbackします。
rollbackするまでは更新した値になっていますが、rollback後は元に戻ります。
>>> tokyo = city.find_one(Name='Tokyo') >>> db.begin() # トランザクション開始 >>> tokyo['Population'] = 114514 # 人口更新 >>> city.update(tokyo, ['ID']) # IDが一致するレコードを更新 1 >>> print(city.find_one(Name='Tokyo')['Population']) # トランザクション内では更新されている 114514 >>> db.rollback() # トランザクション破棄 >>> print(city.find_one(Name='Tokyo')['Population']) # トランザクション前の値にもどる 7980230
次に、もう一度トランザクションをはり、夕張市のレコードをinsertし、commitします。
>>> # IDはauto_incrimentで値がはいるので省略 >>> yubari = dict(Name='Yubari',CountryCode='JPN',District='Hokkaido',Population=8033) >>> db.begin() # トランザクション開始 >>> city.insert(yubari) # cityのテーブルに夕張を追加 4080 >>> print(city.find_one(Name='Yubari')) # トランザクション内では挿入されている OrderedDict([('ID', 4080), ('Name', 'Yubari'), ('CountryCode', 'JPN'), ('District', 'Hokkaido'), ('Population', 8033)]) >>> db.commit() # トランザクション終了 >>> print(city.find_one(Name='Yubari')) # トランザクション抜けても値が残っている OrderedDict([('ID', 4080), ('Name', 'Yubari'), ('CountryCode', 'JPN'), ('District', 'Hokkaido'), ('Population', 8033)])
まとめ
datasetを使えば特にsqlを書かずにレコードを取得・追加・更新ができ、OrderedDictにレコードをマッピングしてくれるので、とても楽です。 「SQLは書きたくない!けど、ORMで楽したい!」という方にはとてもとっつきやすいライブラリだと思います。