Mitama Advent Calendar4日目です。
前回→『MitamaでTodoアプリを作ってみる(2) - Modelを作りこむ』
木瓜丸です。今回も引き続きTodoアプリを組んでみましょう。
ルーティング
まずはControllerに配信先のパスを与えます。ルーティング周りはmain.pyに書いてあるので、次の様に書き換えてください。
from mitama.app import App, Router
from mitama.utils.controllers import static_files
from mitama.app.method import view
from mitama.utils.middlewares import SessionMiddleware
from .controller import HomeController, TodoController
class App(App):
# name = 'MyApp'
# description = 'This is my App.'
router = Router(
[
view("/", HomeController),
view("/create", TodoController, 'create'),
view("/<id>/done", TodoController, 'done'),
view("/static/<path:path>", static_files()),
],
middlewares = [SessionMiddleware]
)
今回はHomeControllerとTodoControllerという2つのコントローラーを作成します。
viewという関数にパス、コントローラークラス、メソッド名を与えると、GETとPOSTのリクエストを受け付けるルーティング先を作成できます。
view("/", HomeController),
view("/create", TodoController, 'create'),
view("/<id>/done", TodoController, 'done'),
view("/static/<path:path>", static_files()),
メソッド名を指定しない場合はhandleメソッドが呼び出されます。
middlewaresにミドルウェアの配列を入れておくと、順番にリクエストがミドルウェアを経由します
middlewares = [SessionMiddleware]
上記のようにSessionMiddlewareをimportして与えると、ログインしているかを確認し、勝手にログイン画面に飛ばしてくれます。
Controllerを作る
ルーティング先を設定したら、次にコントローラーを作りましょう。controller.pyを次のように書き換えてください。
from mitama.app import Controller
from mitama.app.http import Response
from .model import Todo
import dateutil.parser
class HomeController(Controller):
def handle(self, request):
template = self.view.get_template("home.html")
todos = Todo.list()
return Response.render(template, {
'todos': todos
})
class TodoController(Controller):
def create(self, request):
template = self.view.get_template("create.html")
if request.method == 'POST':
post = request.post()
try:
todo = Todo()
todo.subject = post['subject']
todo.node = request.user
todo.dead = dateutil.parser.parse(post['dead'])
todo.done = False
todo.create()
return Response.redirect(self.app.convert_url('/'))
except KeyError as err:
return Response.render(template, {
'err': err
})
return Response.render(template)
def done(self, request):
todo = Todo.retrieve(int(request.params['id']))
todo.done = True
todo.update()
return Response.redirect(self.app.convert_url('/'))
先程のルーティング先に設定したクラスとメソッドを作り込みます。
HomeController
class HomeController(Controller):
def handle(self, request):
template = self.view.get_template("home.html")
todos = Todo.list()
return Response.render(template, {
'todos': todos
})
前回作成したTodoモデルをすべて取得して表示しています。ModelClass.list()と書くことですべて取得することができます。
ちなみに、検索をしたい場合はFlask SQLAlchemyのようにフィルターを書けることができます。
done_todos = Todo.query.filter(Todo.done == False).all()
TodoController
class TodoController(Controller):
def create(self, request):
template = self.view.get_template("create.html")
if request.method == 'POST':
post = request.post()
try:
todo = Todo()
todo.subject = post['subject']
todo.node = request.user
todo.dead = dateutil.parser.parse(post['dead'])
todo.done = False
todo.create()
return Response.redirect(self.app.convert_url('/'))
except KeyError as err:
return Response.render(template, {
'err': err
})
return Response.render(template)
def done(self, request):
todo = Todo.retrieve(int(request.params['id']))
todo.done = True
todo.update()
return Response.redirect(self.app.convert_url('/'))
POSTされた内容を取得するには、request.post()メソッドを使用します。
また、ログインしているMitamaのユーザーの情報を取得したい場合は、request.userプロパティにアクセスすることで簡単に取得することができます。
前回作成したモデルで、Todo.nodeの型にNodeを指定したため、この中に直接Userを入れることができます。
Mitamaではmitama.jsonに指定したpathによって配信されるURLが変わってしまうので、URLを扱う上では注意する必要があります。
return Response.redirect(self.app.convert_url('/'))
Controller.app.convert_uriを実行することで、URLのパスの先頭を調整することができます。
HTMLテンプレートを書く
最後に、HTMLテンプレートを書いてみましょう。
templatesの中に先程のコントローラー内で指定したファイルを作成し、書き込んでください。
今回はエラーの表示やCSRF対策は省略します。
templates/home.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
<div>
<a href='{{url('/create')}}'>Todoを作成</a>
</div>
<div>
{% for todo in todos %}
<div>
<h3>{{todo.subject}}</h3>
<div>{{todo.node.name}}</div>
<div>期日: {{todo.dead}}</div>
{% if todo.done %}
<div>完了済み</div>
{% else %}
<form action='{{url('/'+todo._id|string+'/done')}}' method='POST'>
<button>完了</button>
</form>
{% endif %}
</div>
{% endfor %}
</div>
</body>
</html>
テンプレート内部でも、URLのパスの先頭はurl関数を使って調整することができます。
templates/create.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
<form method='POST'>
<dl>
<dt>題名</dt>
<dd>
<input type='text' name='subject' required/>
</dd>
<dt>期日</dt>
<dd>
<input type='datetime-local' name='dead' required/>
</dd>
</dl>
<button>作成</button>
</form>
</body>
</html>
動かしてみる
mitama runを実行し、アクセスしてみましょう。まず、ログインを求められると思います。
ログインしてアプリを開くと、Todoを作成するというリンクだけの画面がでてきます。そこのリンクをクリックし、フォームを埋めて送信し、Todoができているか確認しましょう。
完了を押して表示が切り替われば成功です。
終わりに
ここまでで一通り動くものを作ることができましたね!ユーザーの認証やユーザーを扱うモデルの作り込みは振る舞いを決めてしまっているので、内製システムなどは簡単に作り込むことができます。
これからアドベントカレンダーの中で様々なお便利機能を紹介したいと思います。是非見ていってください(^^)
追伸
よかったらMitamaのリポジトリにスター付けてください