【dataset】pythonでDB処理を楽チンに！【ORM】【python】

# はじめに
pythonからデータベースをいじる時に、クエリをベタ書きするんじゃなくてORMを利用することでコードがすっきりするのでサーバーサイドやデータサイエンスに興味ある人にはマストだと思います。


# ORMとは？
オブジェクト関係マッピング(Object-Relational Mapping)の略称。<br>
言語からSQLを操作する時に、SQLドライバを入れてクエリ書いて実行する(Ex. mysql-connecter)という方法がある。<br>
でも、これだと上に書いたように処理が多い時に綺麗にコードが書きづらい！<br>

ってことでそれを解消するのにORMが役にたつ！<br>
簡単に言うとSQLを関数的に使うことができる。<br>
今回使うpython用ORMは【dataset】<br>

# 環境
```
OS：MacOS OSX10.11.6
言語：python3.5.2
ORM：dataset0.8.0
DBMS:mysql
```

# 導入
これだけ。

```
sudo pip install dataset
```

だけど、MySQLdbがimportできない！っていうエラー出ることがあります。<br>
その時は、以下のパッケージをインストールしてください。<br>

```
pip install mysqlclient
```

これで、python3でMySQLdbをimportすることができてdatasetをインストールすることができると思います。

# 実践
SQL文と対応させながら基本的な使い方を説明します。<br>
今回は練習用に研究室のデータベースサーバー【db01】を使います。<br>
データベースは【TrainLine】で、小田急線の駅名が入っています。<br>
![__________2017-04-15_22.43.26.png](https://qiita-image-store.s3.amazonaws.com/0/148317/ffddab3b-f219-8a32-d34f-e856275ea883.png)


## 接続
まず、DBと接続します。<br>

```python3
import dataset

# 対象のDBを指定
DBMS = 'mysql'
USER = 'root'
PASS = '~'
HOST = '対象DBのIPアドレス'
DB     = 'TrainLine' 
TABLE='Station_Name'
# 文字コードで悩む人へ
CHARSET = 'utf8'

db = dataset.connect('{0}://{1}:{2}@{3}/{4}?charset={5}'.format(DBMS, USER, PASS, HOST, DB, CHARSET))
table = db[TABLE]
```

DBMS：MySQL,PostgreSQL,sqliteとか対象のDBMSを指定<br>
USER：ログインしたいユーザー名を指定<br>
PASS：パスワードが必要なユーザーの時は指定<br>
HOST：サーバーのアドレスを指定(IPアドレスでも可)<br>
DB：使用するDB名を指定<br>
TABLE：使用するテーブル名を指定<br>
<br>
これで実行してエラーが出なければ接続完了。<br>
次に実際にSQLを実行していきます。

## SELECT文(複数)
複数レコードをselectする時はfind()を使います。<br>

```python
# SQL：SELECT * FROM Station_Name
results = table.find()
for record in results:
    # OrderDict型で1レコードずつ出力される
    print(record)
    # カラムを指定することで特定の情報を引き出せる
    print(record['Station_ID'])
    print(record['Name'])
```

selectされたデータはOrderDict型で返ってきます。<br>
ちなみにOrderDict型は、順番を保持した辞書型のデータです。<br>
複数のレコードをselectしているため、for文を使うことで1レコードずついじれます。<br>
1レコードに対して、カラムを指定することで情報を抽出できます。<br>

```:terminal
===SQL：SELECT * FROM Staion_Name===
OrderedDict([('Station_ID', 2500101), ('Name', '新宿')])
2500101
新宿
OrderedDict([('Station_ID', 2500102), ('Name', '南新宿')])
2500102
南新宿
OrderedDict([('Station_ID', 2500103), ('Name', '参宮橋')])
2500103
参宮橋
OrderedDict([('Station_ID', 2500104), ('Name', '代々木八幡')])
2500104
代々木八幡
OrderedDict([('Station_ID', 2500105), ('Name', '代々木上原')])
2500105
代々木上原
・・・
```

## WHERE句での指定SELECT
SQLで特定の情報を持つレコードをselectするのにWHERE句を使います。<br>
このORMではfind関数の引数にカラムと値を指定することで実現できます。<br>

```python
## where句での指定
# SQL：SELECT * FROM Staion_Name WHERE Station_ID = 2500101
results = table.find(Station_ID=2500101)
for record in results:
    print(record)
```


```:terminal
===SQL：SELECT * FROM Staion_Name WHERE Station_ID = 2500101===
OrderedDict([('Station_ID', 2500101), ('Name', '新宿')])
```

## LIMITの指定も可能
複数レコードをselectする際にレコード数を制限することができる。<br>

```python
## _limit変数でselectの数を制限
# SQL：SELECT * FROM Station_Name limit 10 
results = table.find(_limit = 10)
for record in results:
    print(record)
```

```:terminal
===SQL：SELECT * FROM Staion_ID = 2500101===
OrderedDict([('Station_ID', 2500101), ('Name', '新宿')])
```

## SELECT文(単一)
単一のレコードだけをselectする時はfind_one()を使うとfor文を使わないでデータをいじれる。<br>

```python
# SQL：SELECT * FROM Staion_ID = 2500101
print(table.find_one(Station_ID = 2500101))
```


```:terminal
===SQL：SELECT * FROM Staion_Name limit 10===
OrderedDict([('Station_ID', 2500101), ('Name', '新宿')])
OrderedDict([('Station_ID', 2500102), ('Name', '南新宿')])
OrderedDict([('Station_ID', 2500103), ('Name', '参宮橋')])
OrderedDict([('Station_ID', 2500104), ('Name', '代々木八幡')])
OrderedDict([('Station_ID', 2500105), ('Name', '代々木上原')])
OrderedDict([('Station_ID', 2500106), ('Name', '東北沢')])
OrderedDict([('Station_ID', 2500107), ('Name', '下北沢')])
OrderedDict([('Station_ID', 2500108), ('Name', '世田谷代田')])
OrderedDict([('Station_ID', 2500109), ('Name', '梅ヶ丘')])
OrderedDict([('Station_ID', 2500110), ('Name', '豪徳寺')])
```

## INSERT文(単一)
SQLだと覚えにくいINSERT文もORMを使うと直感的に操作できます。<br>
INSERTしたいレコードデータは、dict型で生成します。

```python
# INSERT文：insert()
# SQL：INSERT INTO Staion_Name VALUES (2500200, '新大久保')
## insertしたいレコード
data = dict(Station_ID=2500200, Name='新大久保')
table.insert(data)
for record in table.find(Station_ID=2500200): print(record)
```

<br>

```:terminal
===SQL：INSERT INTO Staion_Name VALUES (2500200, '新大久保')===
OrderedDict([('Station_ID', 2500200), ('Name', '新大久保')])
OrderedDict([('Station_ID', 2500200), ('Name', '新大久保')])
```


## INSERT文(複数)
複数のレコードをいっきにINSERTする方法が以下です。

```python
## 複数のinsert
print("===複数insert===")
table.insert_many([dict(Station_ID=2500201, Name='西日暮里'), dict(Station_ID=2500202, Name='五反田')])
time.sleep(3)
for ID in [2500201, 2500202]:print(table.find_one(Station_ID=ID))
```

<br>

```:terminal
===複数insert===
OrderedDict([('Station_ID', 2500201), ('Name', '西日暮里')])
OrderedDict([('Station_ID', 2500202), ('Name', '五反田')])
```

## UPDATE文
INSERT文同様にレコードデータを生成します。<br>
ポイントは生成したレコードとデータベースにあるUPDATEしたいレコードの値が共通のカラム(下ので例では、Station_ID)をupdate関数の第２引数に指定することです。<br>

```python
# UPDATE文：update()
# SQL：UPDATE Staion_Name SET (Name = 渋谷) WHERE Station_ID = 2500201
## updateしたいレコード
data = dict(Station_ID=2500201, Name='渋谷')
## updateするためのカラムを指定(第2引数)
table.update(data, ['Station_ID'])
print(table.find_one(Station_ID=2500201))
```

<br>

```:terminal
===SQL：UPDATE Staion_Name SET Name = 渋谷 WHERE Station_ID = 2500201===
OrderedDict([('Station_ID', 2500201), ('Name', '渋谷')])
```

## DELETE文
削除したいレコードがあるときは、delete()を実行する。<br>
delete関数の第1引数にレコードのプライマリキーのカラムと値を指定して削除ができます。<br>

```python
# DELETE文：delete()
# SQL：DELETE FROM Station_Name WHERE Station_ID = 2500202
for ID in [2500200, 2500201, 2500202]: table.delete(Station_ID=ID)
for ID in [2500201, 2500202]:print(table.find_one(Station_ID=ID))
```

<br>

```:terminal
===SQL：DELETE FROM Station_Name WHERE Station_ID = 2500202===
None
None
```

## COUNT文
カラムの値を指定することでその値を持つレコード数を数えることができます。<br>
このORMではWHERE句の不等式での指定ができないため、レコードが存在するかどうかに使えそう。<br>

```python
# COUNT文：count()
# SQL：SELECT COUNT(*) FROM Station_Name WHERE Station_ID = 2500101
## WHERE句は等式でしか指定できないためレコードを確かめるのとかに使える
print(table.count(Station_ID = 2500101))
print(table.count(Station_ID = 2500301))
```

<br>

```:terminal
===SQL：SELECT COUNT(*) FROM Station_Name WHERE Station_ID = 2500101===
1
0
```


# 終わりに
これでpython用ORM【dataset】の説明終わります。<br>
最低限の使い方はわかると思います。<br>
他にも機能を使いたければ、直接gitをリファレンスするといいと思います。<br>
<br>
ORMは知っておいて損はない知識・技術だと思うので、自分が使い慣れてる言語で1つは身につけておくと良いと思います。<br>

読んでくれた方の参考になれば幸いです。

# 参考URL
* [【dataset】GIthub](https://github.com/pudo/dataset)<br>