簡単なToDoリストアプリを作成しながら、前回は取り上げなかったFlask(やJinja2)のさまざまな機能について見ていく。
連載「Visual Studio Codeで始めるPythonプログラミング」
前回は、Visual Studio Code(以下、VS Code)でPython用のWebアプリ開発フレームワーク「Flask」を利用して、簡単なアプリを作成しながら、Flaskの基礎、VS CodeでFlaskを使用する際の基本を見た。今回は、ToDoリストを入力、表示する(簡単な)Webアプリを作りながら、以下の事項について見ていこう。
なお、本稿ではvenvモジュールを利用して、仮想環境「myenv」を作成し、その環境にFlaskをインストールしている。仮想環境の作成とFlaskのインストールについては前回の記事を参照されたい。また、今回はWindows版のVS Code 1.25.1(Python拡張機能をインストール済み)で動作を検証している。
以下に今回作成するToDoリストアプリの完成形を示す。
画面下部にあるテキストボックスにToDoリストに登録する項目の名前を入力して、[submit]ボタンをクリックすれば、それがリストに追加され、画面に表示される。[done!]リンクをクリックすれば、打ち消し線でそれを完了したことを示し、[delete]ボタンをクリックすれば、その項目がリストから削除される。[delete all done items]ボタンは、完了した項目を一括してリストから削除するためのものだ。
ここでは、ToDo項目をToDoItemクラス、それらをまとめたToDoリストをToDoListクラスで管理する(todo.pyファイル)。また、ToDoリストアプリの本体となるapp.pyではtodo.pyファイルからToDoListクラスをインポートするような形にしている。Webページの表示には、前回と同様にtemplatesフォルダにJinja2テンプレートを配置する。今回は「showtodo.html」という名前にしてある。
それ以外には、静的ファイルをFlaskで利用する例としてCSSファイルを作成してみよう([submit]ボタンと[delete all done items]ボタンはこのCSSファイルを利用して描画している)。
最終的なファイル構成は次のようになる。
まずは、ToDoリストを管理する2つのクラスについて見ていこう。
今も述べた通り、ToDoリストはToDoItemクラスとToDoListクラスの2つのクラスで管理する。コードは次のようになっている(2タブそろえとなっているのは、本フォーラムでの仕様なので気にしないでほしい)。なお、以下のリストにはバグを仕込んであるので、後でデバッグ実行をしながら、そのバグをつぶしてみよう。
class ToDoItem:
item_id = 0
def __init__(self, title):
self.title = title
self.done = False
self.item_id = ToDoItem.item_id
ToDoItem.item_id += 1
class ToDoList:
def __init__(self):
self.todolist = []
def add(self, title):
item = ToDoItem(title)
self.todolist.append(item)
def delete(self, item_id):
item = [x for x in self.todolist if x.item_id == item_id]
del item[0]
def update(self, item_id):
item = [x for x in self.todolist if x.item_id == item_id]
item[0].done = not item[0].done
def get_all(self):
return self.todolist
def delete_doneitem(self):
self.todolist = [x for x in self.todolist if not x.done]
ToDoItemクラスでは、内部で使用するID(item_id)、ToDo項目の名前(title)、完了したかどうか(done)をメンバとして持つ。IDはクラス変数となっていて、項目が追加されるたびにインクリメントするようになっている。
ToDoListクラスは、ToDoItemを含むリスト(self.todoList)と、それを操作する以下のメソッドを定義している。
ToDoListクラスは、Webアプリ本体に対するインタフェースとなり、app.pyファイルでは上記のメソッドを呼び出してToDoリストを操作する。
Webアプリの本体のコードは次のようになる。
from flask import Flask, render_template, redirect, request
from todo import ToDoList
app = Flask(__name__)
todolist = ToDoList()
@app.route("/")
def show_todolist():
return render_template("showtodo.html", todolist=todolist.get_all())
@app.route("/additem", methods=["POST"])
def add_item():
title = request.form["title"]
if not title:
return redirect("/")
todolist.add(title)
return redirect("/")
@app.route("/deleteitem/<int:item_id>")
def delete_todoitem(item_id):
todolist.delete(item_id)
return redirect("/")
@app.route("/updatedone/<int:item_id>")
def update_todoitemdone(item_id):
todolist.update(item_id)
return redirect("/")
@app.route("/deletealldoneitems")
def delete_alldoneitems():
todolist.delete_doneitem()
return redirect("/")
コード冒頭部分は、前回も見たように、flaskモジュールからFlaskクラスやメソッドなどをインポートしている。前回はFlaskクラスとrender_templateメソッドのみをインポートしていたが、今回はこれらに加えて次の2つをインポートしている。
今回は、ユーザーがToDoリストを操作すると、redirectメソッドを用いて全てのリクエストをアプリのルート("/")にリダイレクトする(「return redirect("/")」行)。また、ルートでは先ほども述べた通り、showtodo.html(Jinjaテンプレート)を利用して、ToDoリストに含まれる項目を描画している。
requestオブジェクトには、リクエストに関する情報が含まれる。今回は、Webページではフォームを利用して、ToDo項目を送信しているが、その情報(キー/値)はrequest.formというディクショナリから取得できる。そこで、今回はrequestオブジェクトをインポートしている。
これらをインポートし、アプリのインスタンスを作成した後にあるのは、上記ToDoListクラスのインスタンス(todolist)の作成と、ToDoListインスタンスを操作する(ユーザーの要求を処理する)関数群だ。ルーティング情報が付加され、ユーザー要求を処理するこれらの関数のことを、Flaskでは「ビュー関数」と呼んでいる。本稿では、以下の5つのビュー関数を定義している。
show_todolist関数は、todolist(ToDoListクラスのインスタンス)から全ての項目を取得して(get_allメソッド呼び出し)、showtodo.htmlテンプレートを使って、ToDo項目をWebページに表示している。
他の関数は、ユーザーからのリクエストに応えて、基本的に上で説明したToDoListクラスの各メソッドを呼び出すだけだ(関数名とToDoListクラスのメソッド名を一致させておいた方がよかったかもしれない)。「@app.route」デコレータで付加するルーティング情報についても、前回に説明したものが基本的には使われている(「<int:item_id>」という「コンバーター」については前回の説明を参照のこと)。
注意が必要なのは、add_item関数だろう。
@app.route("/additem", methods=["POST"])
def add_item():
title = request.form["title"]
if not title:
return redirect("/")
todolist.add(title)
return redirect("/")
@app.routeデコレータには、これまでとは異なり、methodsキーワード引数が付加されている。その内容を見れば分かる通り、これは「/additemというURLにPOSTメソッドでリクエストが送られた場合」に処理を行うことを意味している。関数内部では、先ほども述べたように、request.formディクショナリから「title」をキーとする値を取得し、それがなければルートにリダイレクトし、そうでなければそれを項目の名前(title)としてtodolistに項目を追加している。
次に、このアプリで描画するWebページについて見てみよう。
Copyright© Digital Advantage Corp. All Rights Reserved.