search
LoginSignup
0

More than 1 year has passed since last update.

posted at

updated at

MitamaでTodoアプリを作ってみる(3) - ControllerとViewを作る

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を実行し、アクセスしてみましょう。まず、ログインを求められると思います。
2020-12-03-190133_1920x1080_scrot.png

ログインしてアプリを開くと、Todoを作成するというリンクだけの画面がでてきます。そこのリンクをクリックし、フォームを埋めて送信し、Todoができているか確認しましょう。

2020-12-03-190322_1920x1080_scrot.png

完了を押して表示が切り替われば成功です。

2020-12-03-190808_1920x1080_scrot.png

終わりに

ここまでで一通り動くものを作ることができましたね!ユーザーの認証やユーザーを扱うモデルの作り込みは振る舞いを決めてしまっているので、内製システムなどは簡単に作り込むことができます。

これからアドベントカレンダーの中で様々なお便利機能を紹介したいと思います。是非見ていってください(^^)

追伸

よかったらMitamaのリポジトリにスター付けてください

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0