「Orator」を触ってみたのでメモ

# [Orator](https://orator-orm.com/)　概要
* PythonのORM
    * `The Orator ORM provides a simple yet beautiful ActiveRecord implementation.`
    * PHPのWeb Frameworkである[Laravel](https://laravel.com/)のDB操作にインスパイアされているらしい。
* Migrationもサポートしている。
* 2016年1月時点で、**「ver7.1」** がリリースされている。
    * 2015年3月に「ver0.1」がリリース。
    * **怒涛のリリースにより、この記事の情報が役に立たなくなっている可能性もありうるので注意。**
* Flaskをサポートした [Flask-Orator](http://flask-orator.readthedocs.org/en/latest/index.html) なるものも存在する。
* 公式ドキュメントがなんだかオシャレでかっこいい。
* もっと詳しく！とか正式な情報を求めている方は公式ドキュメントへ、どうぞ。

# このメモに書いている範囲
* DBの設定
* テーブルの作成
* Modelとなるクラスの作成
* DBへのデータの保存

# メモ詳細
## DBの設定
dirverとdatabaseを指定する。現時点でサポートされているDBは、PostgreSQL, MySQL, SQLiteの3つ。driver, user(DBのユーザ), password(DBのパスワード), database(DBの所在)などを設定する。
例えば、SQLiteの場合は以下のように定義できる。

```py3
DATABASES = {                                                                   
   'development': {                                                            
        'driver': 'sqlite',  
        'database': '/tmp/authgate.sqlite'
    }
}
```

## テーブルの作成
DatabaseManagerのインスタンスを作成し、そのインスタンスでDBにアクセスすることができる。インスタンスを作成する際に、DBの定義を渡す。

```py3
from orator import DatabaseManager

DATABASES = {                                                                   
   'development': {                                                            
        'driver': 'sqlite',  
        'database': '/tmp/authgate.sqlite'
    }
}
db = DatabaseManager(DATABASES)
```

Schema Builderで、DBに対して操作できる。DatabaseManagerのインスタンスを渡してSchemaインスタンスを作成する。
あとはSchemaインスタンスを使って、定義したいテーブルのカラムに合わせたメソッドを呼び出していく。

```py3:create-database.py
from orator import DatabaseManager
from orator import Schema

DATABASES = {                                                                   
   'development': {                                                            
        'driver': 'sqlite',  
        'database': '/tmp/authgate.sqlite'
    }
}

db = DatabaseManager(DATABASES)
schema = Schema(db)

with schema.create('samples') as table:
    table.increments('id')
    table.string('string_column').nullable()
    table.integer('integer_column')
```

作成した上記ファイルを実行すると、定義に基づいたDBが作成される。

```shell-session
$ python create-database.py
```

詳細は、[OratorのSchema Builderの項](
http://orator.readthedocs.org/en/latest/schema_builder.html)を参照。

## Modelとなるクラスの作成
### 基本構文
OratorのModelクラスを継承して作成する。
以下の例は、先ほど作成した **string_column**、**integer_column**という2つのカラムを持つ**samples** テーブルにマッピングされたクラスとなる。

```py3
class Sample(Model):                                                              
    __table__ = 'samples'                                                                                            
    __fillable__ = ['string_column'] 
    __guarded__ = ['integer_column'] 
```

#### `__fillable__`　と `__guarded__`
mass-assignmentを許可するか否かで使い分ける。`__guarded__`で定義したパラメータは、mass-assignmentが許可されない。
すべての属性に対して、mass-assignmentを許可したくない場合は、以下のように記述できる。

```py3
class AllBlockSample(Model):                                                              
    __table__ = 'samples'
    __guarded__ = ['*']
```

### そのほか色々なプロパティをつまみ食いする
Oratorでは、`__プロパティ名__`で表現される様々なプロパティが存在する。プロパティを設定するだけで実現できることもあるので、一読しておくと幸せになれそう。以下にその一例をつらつらと。

#### **・`__hidden__`プロパティ**
以下のようなモデルを例に考える。

```py3
class User(Model):
    __table__ = 'user'
    __fillable__ = ['login_id', 'password', 'name']
    __guarded__ = ['access_token']
    __hidden__ = ['password', 'access_token']
```

Oratorでは、クラスのプロパティを列挙する専用のメソッドが用意されている。

* `to_json()`：json形式
* `seliarize()`：dictionary形式

`__hidden__` に設定されたものは、上記メソッド利用時に表示されない。Userクラスの例だと、login_id, nameだけが表示される(idは、**PRIMARY KEY** )。
もちろん、Python標準の `__dict__` や `vars()` には`__hidden__`の効果は適用されない。

#### **・`__timestamp__`プロパティ**
Oratorではデフォルトでテーブルの作成時間(**created_at**)と更新時間(**updated_at**)が記録されるカラムが自動生成される。自動生成をしたくない場合は、`__timestamps__ = False`を追加する。

```py3
class SampleModel(Model):
    __table__ = 'samples'
    __timestamps__ = False
```

## DBへのデータの保存
save() もしくは create() で保存することができる。

```py3
sample_model = SampleModel()
sample_model.string_column = "test"
sample_model.integer_column = 100
sample_model.save()

sample_model = SampleModel.create(string_column='test')
```

createの引数に、`__guarded__`パラメータを渡すことはできない。仮に渡したとしても無視され、該当するパラメータはNoneのままとなる。

また、Modelクラス作成時に `__guarded__ = ['*']` を指定している場合、createメソッド呼び出し時に`orator.exceptions.orm.MassAssignmentError`が発生する。

save() と create() の細かな違いについては調査不足で理解できていない。

# 終わりに
設定方法やクラスの定義方法等についてメモを書き連ねた。「Migration」と「Select/Delete/Updateのクエリ発行」についても書き連ねるつもりだったが力尽きてしまった。また別の機会に。