SQLAlchemy で Delete するには？

今回は **SQLAlchemy** で **Delete** のやり方を書きます。

## 動作環境
- Mac OS X 10.11.5
- Python 3.5.1
- MySQL Ver 14.14 Distrib 5.7.11, for osx10.11 (x86_64) using  EditLine wrapper
- SQLAlchemy 1.1.0
- PyMySQL 0.7.4

## サンプルコード
まずはシンプルに、テーブルから「name」カラムが "桑田 結子" となっているデータを検索して、削除します。

```py3:sqlalchemy_delete.py
# -*- coding:utf-8 -*-
import sqlalchemy
import sqlalchemy.orm
import sqlalchemy.ext.declarative

Base = sqlalchemy.ext.declarative.declarative_base()

class Student(Base):
    __tablename__ = 'students'
    id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
    name = sqlalchemy.Column(sqlalchemy.String(20))
    kana = sqlalchemy.Column(sqlalchemy.String(40))

def main():
    url = 'mysql+pymysql://root:@localhost/test_db?charset=utf8'

    engine = sqlalchemy.create_engine(url, echo=False)

    # テーブルを作成
    Base.metadata.create_all(engine)

    # セッションを作成
    Session = sqlalchemy.orm.sessionmaker(bind=engine)
    session = Session()

    # 全データ削除
    session.query(Student).delete()

    # データベースに追加するデータリスト
    student_list = [
        Student(id=1, name='石坂 優', kana='いしざか ゆう'),
        Student(id=2, name='杉野 誠一', kana='すぎの せいいち'),
        Student(id=3, name='桑田 結子', kana='くわた ゆうこ'),
        Student(id=4, name='栗原 愛', kana='くりはら あい'),
        Student(id=5, name='佐久間 仁', kana='さくま じん'),
    ]

    # データリストを一括追加
    session.add_all(student_list)

    # 削除対象のデータを検索
    found_student = session.query(Student).filter_by(name='桑田 結子').first()

    # 指定したデータを削除
    session.delete(found_student)

    # テーブルの全データを出力
    print_all_students(session)

    # データベースに反映
    session.commit()

# テーブルの全データを出力する関数
def print_all_students(session):
    students = session.query(Student).all()
    for student in students:
        print('%d, %s %s' % (student.id, student.name, student.kana))

if __name__ == '__main__':
    main()
```

次に応用として、フィルタ指定や IN 句指定でデータを削除します。

```py3:sqlalchemy_delete.py
# -*- coding:utf-8 -*-
import sqlalchemy
import sqlalchemy.orm
import sqlalchemy.ext.declarative

Base = sqlalchemy.ext.declarative.declarative_base()

class Student(Base):
    __tablename__ = 'students'
    id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
    name = sqlalchemy.Column(sqlalchemy.String(20))
    kana = sqlalchemy.Column(sqlalchemy.String(40))

def main():
    url = 'mysql+pymysql://root:@localhost/test_db?charset=utf8'

    engine = sqlalchemy.create_engine(url, echo=False)

    # テーブルを作成
    Base.metadata.create_all(engine)

    # セッションを作成
    Session = sqlalchemy.orm.sessionmaker(bind=engine)
    session = Session()

    # 全データ削除
    print("====== session.query(Student).delete() =====")
    session.query(Student).delete()
    print_all_students(session)

    # データベースに追加するデータリスト
    student_list = [
        Student(id=1, name='石坂 優', kana='いしざか ゆう'),
        Student(id=2, name='杉野 誠一', kana='すぎの せいいち'),
        Student(id=3, name='桑田 結子', kana='くわた ゆうこ'),
        Student(id=4, name='栗原 愛', kana='くりはら あい'),
        Student(id=5, name='佐久間 仁', kana='さくま じん'),
    ]

    # データリストを一括追加
    print("====== session.add_all(student_list) ======")
    session.add_all(student_list)
    print_all_students(session)

    # フィルタで指定して削除
    print("====== session.query(Student).filter(Student.id==2).delete() =====")
    session.query(Student).filter(Student.id==2).delete()
    print_all_students(session)

    # IN 句で指定して削除
    print("====== session.query(Student).filter(Student.id.in_([3, 4])).delete(synchronize_session='fetch') =====")
    session.query(Student).filter(Student.id.in_([3, 4])).delete(synchronize_session='fetch')
    print_all_students(session)

    # データベースに反映
    session.commit()

# テーブルの全データを出力する関数
def print_all_students(session):
    students = session.query(Student).all()
    for student in students:
        print('%d, %s %s' % (student.id, student.name, student.kana))

if __name__ == '__main__':
    main()
```

## まとめ
Session クラスに add_all というメソッドがあるので、全データ削除は delete_all かな？と思われる方もいそうですが、そうではないので注意ですね。

また、delete メソッドの引数 synchronize_session のデフォルト引数は 'evaluate' となっているため、IN 句で指定して削除する場合は 'fetch' を指定しないと例外が発生します。
詳しくは[公式ドキュメント](http://docs.sqlalchemy.org/en/rel_1_1/orm/query.html#sqlalchemy.orm.query.Query.delete)を参照してください。

[エラーメッセージ]
`sqlalchemy.exc.InvalidRequestError: Could not evaluate current criteria in Python. Specify 'fetch' or False for the synchronize_session parameter.`

公式ドキュメント＞Query API＞delete(synchronize_session='evaluate')
http://docs.sqlalchemy.org/en/rel_1_1/orm/query.html#sqlalchemy.orm.query.Query.delete