sqlalchemyによるRDB入門 Ⅱ

[前回](http://qiita.com/zakuro9715/items/68e201d36acc2656a606)は、DBやRDBの基本を勉強しました。

今回はいよいよ、PythonからDBを活用していきましょう。

# 事前準備

sqlalchemyのインストール

```
$ pip install sqlalchemy
```

# データベースの準備
## データベースとの接続

`db.py`を作成し、以下の内容を記述します。

```db.py
from sqlalchemy import create_engine, MetaData

engine = create_engine('sqlite:///db.sqlite3', echo=True)
metadata = MetaData()
metadata.bind = engine
```

一行づつ解説していきます。

```py
engine = create_engine('sqlite:///db.sqlite3', echo=True)
```

DBと接続するためのオブジェクトを作成します。

`create_engine`の第一引数には、接続先DBのURLを指定します。DBのURLが何かについては詳しく説明しませんが、sqliteの場合は`sqlite:///ファイル名`と指定すれば大丈夫です。

また、`create_engine`に`echo=True`を指定することで、ログが出力されるようになります。デバッグ等に役立つので指定しておくとよいでしょう。

原則として接続先のDBは予め作成する必要がありますが、sqliteでは接続先のDBが存在しない場合は自動で作成されます。

```py
matadata = MetaData()
```

メタデータを作成します。

メタデータにはテーブルのスキーマ等が格納されるのですが、よくわからなければ「よくわからないけどDBのデータが入ってる」程度の認識で大丈夫です。

```py
metadata.bind = engine
```

メタデータとDBを結びつけます。

## テーブルの定義

ここでは、`name`カラムと`kcal`カラムを持った`menus`テーブルを作成します。

db.pyを以下のように編集してください。

```db.py
# テーブル定義用のクラスを追加でインポート
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String

engine = create_engine('sqlite:///db.sqlite3', echo=True)
metadata = MetaData()
metadata.bind = engine

# menuテーブルの定義
menus = Table(
  'menus', metadata,
  Column('id', Integer, primary_key=True),
  Column('name', String),
  Column('kcal', Integer)
)
```

`Table`の第一引数にはテーブル名を、第二引数にはメタデータを指定します。それ以降は、カラムを自由に指定することができます。

また、`Column`の第一引数にはカラム名を、第二引数にはカラム型を設定します。また、`id`カラムは主キーに設定しています。

## テーブルの作成

ここまでで、テーブルの定義までは完了しましたが、まだテーブルは作成されていません。

インタラクティブシェルを起動して、以下のようにテーブルを作成しましょう。

```py
>>> import db
>>> db.metadata.create_all()
```

## 発展: メタデータの意義

ここまでで、あまり説明できずにいたメタデータの意義がようやく見えてきます。metadataはテーブルの構造と`engine`の両方を持っているので、テーブルを作成することができるのです。

もし、この時点で`metadata`と`engine`が紐付けられていないなら、次のように明示的に指定する必要があります。

```py
>>> import db
>>> db.metadata.create_all(engine)
```

また、テーブルの作成だけでなく、今後DBを操作する際にもやや面倒になるため、`metadata`と`engine`は予め結びつけておくことをおすすめします。

## まとめ

- DBを使用するためには、DBとの接続・テーブルの定義・テーブルの作成を行う必要がある。
- メタデータはテーブルの情報を保持する。

やや難しかったかもしれませんが、DBを使用するための準備はこれで完了です。もしここまでの内容がよくわかっていなくても、次の内容に直ちに支障はありません。しかし、理解しておくべき内容なので、しばらく経ってから改めて読み返してみてください。

# DBの活用

## データの作成

データを作成する操作を、RDBMSでは**insert**と呼びます。作成(create)ではなく挿入(insert)であることに注意してください。これは、新しいデータを作成するというのは、テーブルに新しい行を挿入することだと考えると覚えやすいかもしれません。

sqlalchemyでは、`insert`は以下のように実行します。

```py
>>> import db
>>> db.menus.insert().execute(name='カレーメシ', kcal=500)
```

`insert`を呼び出したあとに更に`execute`を呼び出していることに注意してください。`execute`を呼び出すまでは操作が実行されることはありません。

これは、他の操作についても同様です。DBに対する操作は、原則として`execute`を呼び出すまでは実行されません。

## データの取得

データを取得するには、`select`を使用します。

まずは、以下のコードを実行してみてください。

```
>>> import db
>>> db.menus.select().execute().fetchall()
[(1, 'カレーメシ', 500)]
```

`insert`と同様に、`execute`を呼び出すまではデータは取得されません。また、`execute`の結果に対して更に`fetchall`を呼び出していることに注意してください。

なお、返り値はタプルのリストのように見えますが、そうではありません。`res[0][0]`というように、タプルのように扱うこともできますが、`res[0]['id']`の用に、カラム名で取得することも可能です。

また、全データを取得するだけでなく、`where`を利用して取得するデータを絞り込んだり、`order_by`を使用してソートしたり、`limit`を使用して取得するデータの件数を制限することもできます。

例えば、以下のコードでは「カロリーが100kcal未満の献立を5件まで取得してカロリー順にソートする」ことができます。

```py
>>> import db
>>> db.menus.select().where(db.menus.c.kcal < 100).order_by('kcal').limit(5).execute().fetchall()
```

このような絞り込みは、データの更新や削除でも同様です。

## データの更新

`update`を使用します。基本は`insert`と同じですが絞り込みを行わない場合は全データが対象になることに注意してください。

以下のコードは、`id`が1のデータの`kcal`を1000に更新します。

```py
>>> import db
>>> db.menus.update().where(db.menus.c.id == 1).execute(kcal=1000)
```

## データの削除

`delete`を使用します。基本は、`insert`、`update`と同様です。

以下のコードは、`kcal`が1000より大きいデータをすべて削除します。

```py
>>> import db
>>> db.menus.delete().where(db.menus.c.kcal > 1000).execute()
```

## まとめ

- DBに対する操作は、`execute`を呼び出すまでは実行されない。
- select, update, deleteでは、データを絞り込むことができる。絞り込みを行わない場合はすべてのデータが対象になる。

前半に引き続き、やや難し目の内容でした。全部を理解できていなくても問題ありません。簡易的なリファレンスとしても活用できるので、迷ったら再確認しましょう。

# 今回のまとめ

今回は、前回に比べて難し目の内容でした。一度で理解できなくても問題ありません。また、理解したつもりになっていても、実際にコードを書こうとするとどう書けばよいかわからなくなることもあるので、そのようなときは改めて読み返してみましょう。