連載
» 2018年07月31日 05時00分 公開

Visual Studio Codeで始めるPythonプログラミング:VS CodeとFlaskで作成するToDoリストアプリ (1/3)

簡単なToDoリストアプリを作成しながら、前回は取り上げなかったFlask(やJinja2)のさまざまな機能について見ていく。

[かわさきしんじ,Insider.NET編集部]
「Visual Studio Codeで始めるPythonプログラミング」のインデックス

連載「Visual Studio Codeで始めるPythonプログラミング」

 前回は、Visual Studio Code(以下、VS Code)でPython用のWebアプリ開発フレームワーク「Flask」を利用して、簡単なアプリを作成しながら、Flaskの基礎、VS CodeでFlaskを使用する際の基本を見た。今回は、ToDoリストを入力、表示する(簡単な)Webアプリを作りながら、以下の事項について見ていこう。

  • フォームからの値の取得方法や@app.routeデコレータのmethodsキーワード引数
  • Jinja2テンプレートの構文({% if %}、{% for %}など)
  • Flaskでの静的ファイル(スタイルシートなど)の利用方法
  • VS CodeでのFlaskアプリのデバッグ実行

 なお、本稿ではvenvモジュールを利用して、仮想環境「myenv」を作成し、その環境にFlaskをインストールしている。仮想環境の作成とFlaskのインストールについては前回の記事を参照されたい。また、今回はWindows版のVS Code 1.25.1(Python拡張機能をインストール済み)で動作を検証している。

今回作成するToDoリストアプリ

 以下に今回作成するToDoリストアプリの完成形を示す。

今回作成するToDoリストアプリ 今回作成する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リストを管理するクラス

 今も述べた通り、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クラスとToDoListクラス(todo.pyファイル)

 ToDoItemクラスでは、内部で使用するID(item_id)、ToDo項目の名前(title)、完了したかどうか(done)をメンバとして持つ。IDはクラス変数となっていて、項目が追加されるたびにインクリメントするようになっている。

 ToDoListクラスは、ToDoItemを含むリスト(self.todoList)と、それを操作する以下のメソッドを定義している。

  • addメソッド:self.todoListにToDo項目を追加する
  • deleteメソッド:指定されたIDのToDo項目を削除する
  • updateメソッド:指定されたIDのdoneメンバの値を反転する
  • get_allメソッド:ToDo項目を含むリストを返送する
  • delete_doneitemメソッド:doneメンバの値がTrueとなっている項目(完了した項目)をリストから削除する

 ToDoListクラスは、Webアプリ本体に対するインタフェースとなり、app.pyファイルでは上記のメソッドを呼び出してToDoリストを操作する。

Webアプリ本体

 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("/")

ToDoアプリ本体のコード(app.pyファイル)

 コード冒頭部分は、前回も見たように、flaskモジュールからFlaskクラスやメソッドなどをインポートしている。前回はFlaskクラスとrender_templateメソッドのみをインポートしていたが、今回はこれらに加えて次の2つをインポートしている。

  • redirectメソッド:指定したパスにリダイレクトする
  • requestオブジェクト:ユーザーからのリクエストに関する情報を含んだオブジェクト

 今回は、ユーザーがToDoリストを操作すると、redirectメソッドを用いて全てのリクエストをアプリのルート("/")にリダイレクトする(「return redirect("/")」行)。また、ルートでは先ほども述べた通り、showtodo.html(Jinjaテンプレート)を利用して、ToDoリストに含まれる項目を描画している。

 requestオブジェクトには、リクエストに関する情報が含まれる。今回は、Webページではフォームを利用して、ToDo項目を送信しているが、その情報(キー/値)はrequest.formというディクショナリから取得できる。そこで、今回はrequestオブジェクトをインポートしている。

 これらをインポートし、アプリのインスタンスを作成した後にあるのは、上記ToDoListクラスのインスタンス(todolist)の作成と、ToDoListインスタンスを操作する(ユーザーの要求を処理する)関数群だ。ルーティング情報が付加され、ユーザー要求を処理するこれらの関数のことを、Flaskでは「ビュー関数」と呼んでいる。本稿では、以下の5つのビュー関数を定義している。

  • show_todolist関数:showtodo.htmlテンプレートを利用して、todolistに含まれている項目を表示する
  • add_item関数:todolistに項目を追加する
  • delete_todoitem関数:指定されたIDの項目をtodolistから削除する
  • update_todoitemdone関数:指定されたIDの項目の完了状態を変更する
  • delete_alldoneitems関数:完了した項目をtodolistから削除する

 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("/")

add_item関数

 @app.routeデコレータには、これまでとは異なり、methodsキーワード引数が付加されている。その内容を見れば分かる通り、これは「/additemというURLにPOSTメソッドでリクエストが送られた場合」に処理を行うことを意味している。関数内部では、先ほども述べたように、request.formディクショナリから「title」をキーとする値を取得し、それがなければルートにリダイレクトし、そうでなければそれを項目の名前(title)としてtodolistに項目を追加している。

 次に、このアプリで描画するWebページについて見てみよう。

       1|2|3 次のページへ

Copyright© 1999-2018 Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。