Python+BottleでToDoアプリを作ってみた

1月からPythonの勉強を少しずつ始めました。

早速ToDoアプリとかを作ってみたいなーと思っていたところ、Bottleというシンプルなフレームワークを見つけたので、Python+BottleでToDoアプリを作ってみたいと思います。

## やりたいこと
* Bottleを使ってToDoアプリを作る
* ToDoの表示、新規作成、削除ができるようにする

## 開発環境
言語: Python 3.5.2
フレームワーク: Bottle

## ディレクトリ構成
>python-bottle-todo/
  ├ bottle.py
  ├ todo.py
  ├ index.html

Bottleは、`bottle.py`というファイル１つで成り立っています。

https://raw.githubusercontent.com/bottlepy/bottle/master/bottle.py
ここからソースを入手しましょう。

`$ wget https://raw.githubusercontent.com/bottlepy/bottle/master/bottle.py`

## 完成画面

![スクリーンショット 2018-01-09 18.15.35.png](https://qiita-image-store.s3.amazonaws.com/0/24142/d984b32b-173b-9b75-ef1b-b510fb42e99f.png)

## 実装

```python:todo.py
from bottle import route, run, template, redirect, request
import sqlite3

# データベースに接続
dbname = "todo.db"
conn = sqlite3.connect(dbname)
c = conn.cursor()

try:
    # テーブルの作成
    c.execute("DROP TABLE IF EXISTS todo_list")
    c.execute(
        "CREATE TABLE IF NOT EXISTS todo_list (id INTEGER PRIMARY KEY, todo TEXT)")
    # プログラムから試しに１つだけToDoを追加しておく
    c.execute("INSERT INTO todo_list VALUES (1, 'あらかじめ挿入するTODO')")
except sqlite3.Error as e:
    print('sqlite3.Error occurred:', e.args[0])

conn.commit()
conn.close()

# / にアクセスしたら、index関数が呼ばれる
@route("/")
def index():
    todo_list = get_todo_list()
    return template("index", todo_list=todo_list)

# methodにPOSTを指定して、add関数を実装する
@route("/add", method="POST")
def add():
    todo = request.forms.getunicode("todo_list")
    save_todo(todo)
    return redirect("/")

# @routeデコレータの引数で<xxxx>と書いた部分は引数として関数に引き渡すことができます。
# intは数字のみ受け付けるフィルタ
@route("/delete/<todo_id:int>")
def delete(todo_id):
    delete_todo(todo_id)
    return redirect("/")

def get_todo_list():
    conn = sqlite3.connect(dbname)
    c = conn.cursor()
    select = "select id, todo from todo_list"
    c.execute(select)
    todo_list = []
    for row in c.fetchall():
        todo_list.append({
            "id": row[0],
            "todo": row[1]
        })
    conn.close()
    return todo_list

def save_todo(todo):
    conn = sqlite3.connect(dbname)
    c = conn.cursor()
    insert = "insert into todo_list(todo) values(?)"
    c.execute(insert, (todo,)) # todoのあとにカンマをつけないとなぜかエラーになる
    conn.commit()
    conn.close()

def delete_todo(todo_id):
    conn = sqlite3.connect(dbname)
    c = conn.cursor()
    delete = "delete from todo_list where id=?"
    c.execute(delete, (todo_id,))
    conn.commit()
    conn.close()

# テスト用のサーバをlocalhost:8080で起動する
run(host="localhost", port=8080, debug=True, reloader=True)

```

```html:index.html
<!DOCTYPE html>
<html lang="jp">
<head>
  <meta charset="UTF-8">
  <title>ToDoアプリ(Python3 + Bottle)</title>
</head>
<body>
  <h1>ToDoアプリ</h1>
  <p>これはPython3とフレームワークBottleによって作ったToDoアプリです。</p>
  
  <h2>ToDo 新規登録</h2>
  <form name="todo" method="POST" action="/add">
      <input type="text" name="todo_list" placeholder="ToDoを入力" required /><br/>
      <input type="submit" value="追加" />
  </form>

  <h2>ToDo 一覧</h2>
  <table border="1">
  %for todo in todo_list:
    <tr>
      <td>{{todo["id"]}}</td>
      <td>{{todo["todo"]}}</td>
      <td><a href="/delete/{{todo['id']}}">削除</a></td>
    </tr>
  %end
  </table>

</body>
</html>
```

## 実行
```
python todo.py
```
http://localhost:8080/ にアクセスして動いていれば成功です。

## 参考

Bottle: Python Web Framework
https://bottlepy.org/docs/dev/

bottle超初歩入門
https://qiita.com/Gen6/items/949babb51d0cc000dcfb

bottleで始めるWEBアプリの最初の一歩
https://www.slideshare.net/satoshiyamada71697/bottleweb

pythonでbottleを使ってtodoアプリを作ってみた
https://qiita.com/__X68000__/items/b45486ebdf6a65539015

Pythonで一番小さいWEBフレームワークbottle.py その1
http://www.denzow.me/entry/2017/08/30/223926

Pythonでsqlite
https://qiita.com/mas9612/items/a881e9f14d20ee1c0703

Python3でSQLite3を使う – 基本操作からエラー処理までサンプルコード付
https://www.craneto.co.jp/archives/1248/

## あとがき
* 普段はRailsでアプリ作ってますが、それに比べてBottleは単一ファイルでさくっと簡単なアプリが作れるのが魅力的ですね。
* 練習用とか小さい個人アプリ用としては良さそうです。中〜大規模なプロダクトとして使われている例があったら知りたいです。
* 感想、フィードバック、ご指摘じゃんじゃんお待ちしております！