
【第6回】FastAPIチュートリアル: toDoアプリを作ってみよう【予定の追加・削除編】
2021.12.20
FastAPIチュートリアル: toDoアプリを作ってみよう~第6回~
前回の記事「【第5回】FastAPIチュートリアル: toDoアプリを作ってみよう【予定詳細ページ作成編】」の続きです。
今回も、toDo アプリを作る過程で、FastAPI の使い方を学んでいきましょう!
第1回はこちら
予定の追加
前回に引き続き、管理者ページの改良を行なっていきます。
今回は、予定詳細ページから「予定の追加と削除」ができるようにするのが目標です。
では、まずは「予定の追加」から行っていきたいと思います!
ビューを改良
まずは、予定詳細ページ(detail.html)に、新たにフォームを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <!-- ~~ タスク終了に関するフォーム (省略) ~~ --> {% if task | length != 0 %} <button type="submit" class="btn btn-primary">更新する</button> {% else %} <button type="submit" class="btn btn-primary" disabled="disabled">更新する</button> {% endif %} </form> <!-- ここから追記 --> <br> <hr> <br> <h3>予定の追加</h3> <form action="/add" method="post"> <h5>内容</h5> <p><input type="text" size="50" maxlength="200" name="content"></p> <br> <h5>締め切り時間</h5> <p> {{ year }}年{{ month }}月{{ day }}日 <select name="hour"> {% for h in range(25) %} <option value="{{ h }}">{{ h }}</option> {% endfor %} </select>時 <select name="minute"> {% for m in range(61) %} <option value="{{ m }}">{{ m }}</option> {% endfor %} </select>分 </p> <br> <button type="submit" class="btn btn-primary">予定を追加する</button> </form> <!-- ここまで --> {% endblock %} |
予定の追加は「/add」というURLに投げることにしました。
見た目的にはこのような感じになっています。

予定の追加フォーム
/addをコーディング
次に、実際に処理を書いていきましょう!
ルーティングは、いつものごとくです。
1 2 3 4 5 6 7 8 | # urls.py # FastAPIのルーティング用関数 app.add_api_route('/', index) app.add_api_route('/admin', admin, methods=['GET', 'POST']) app.add_api_route('/register', register, methods=['GET', 'POST']) app.add_api_route('/todo/{username}/{year}/{month}/{day}', detail) app.add_api_route('/done', done, methods=['POST']) app.add_api_route('/add', add, methods=['POST']) # new |
コントローラも、大した処理は必要ありません。
フォームデータをうまく処理するだけなので、以下のように書くことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | async def add(request: Request, credentials: HTTPBasicCredentials = Depends(security)): # 認証 username = auth(credentials) # ユーザ情報を取得 user = db.session.query(User).filter(User.username == username).first() # フォームからデータを取得 data = await request.form() year = int(data['year']) month = int(data['month']) day = int(data['day']) hour = int(data['hour']) minute = int(data['minute']) deadline = datetime(year=year, month=month, day=day, hour=hour, minute=minute) # 新しくタスクを生成しコミット task = Task(user.id, data['content'], deadline) db.session.add(task) db.session.commit() db.session.close() return RedirectResponse('/admin') |
動作確認

適当に予定を追加して...

予定が追加されました
予定の削除
間違えて予定を入れてしまった場合、「予定の削除」機能が必要です。
その機能を追加していきましょう!
予定詳細ページに削除ボタンを追加する
今回は、削除機能はフォームではなく、削除ボタン(リンク)で処理することにしました。
予定一覧のテーブルに、新たな列を追加してみましょう。
削除URLは「/delete/{task_id}」としました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <h3>予定一覧</h3> <p>予定を終了した場合はチェックボックスにチェックをしてください。</p> <form action="/done" method="post"> <table class="table"> <thead class="table-dark"> <tr> <th>内容</th> <th>締め切り</th> <th>終了</th> <th></th> <!-- new --> </tr> </thead> <tbody> {% for t in task %} <tr> <td>{{ t.content }}</td> <td>{{ t.deadline }}</td> <td> {% if t.done %} <span class="text-success">終了済</span> {% else %} <input type="checkbox" name="done[]" value="{{ t.id }}"> <span class="text-danger">終了する</span> {% endif %} </td> <td> <a href="/delete/{{ t.id }}" class="btn btn-danger btn-sm">削除</a> <!-- new --> </td> </tr> {% endfor %} </tbody> </table> |
見た目はこのような感じになっています。

削除ボタンの追加
/delete/{task_id}のコントローラを書く
ルーティングです。
1 2 3 4 5 6 7 8 | # FastAPIのルーティング用関数 app.add_api_route('/', index) app.add_api_route('/admin', admin, methods=['GET', 'POST']) app.add_api_route('/register', register, methods=['GET', 'POST']) app.add_api_route('/todo/{username}/{year}/{month}/{day}', detail) app.add_api_route('/done', done, methods=['POST']) app.add_api_route('/add', add, methods=['POST']) app.add_api_route('/delete/{t_id}', delete) # new |
もう大丈夫ですね。
そうしたらコントローラを書きますが、今回はURLパターンで削除したいタスクを取得するだけです。
ただ、部外者が勝手にタスクを削除できないように工夫しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | def delete(request: Request, t_id, credentials: HTTPBasicCredentials = Depends(security)): # 認証 username = auth(credentials) # ログインユーザ情報を取得 user = db.session.query(User).filter(User.username == username).first() # 該当タスクを取得 task = db.session.query(Task).filter(Task.id == t_id).first() # もしユーザIDが異なれば削除せずリダイレクト if task.user_id != user.id: return RedirectResponse('/admin') # 削除してコミット db.session.delete(task) db.session.commit() db.session.close() return RedirectResponse('/admin') |
どうでしょうか?
ここまでくると、データベースの扱いは慣れてきたかと思います。
SQLAlchemyでは、データベースの処理( session.query() や session.add() 、 session.delete() )の後は、 session.close() する、という一連の処理を覚えておきましょう。
データベースに変更を反映させる場合はクローズする前に、 session.commit() です!
第6回:さいごに
今回は、予定詳細ページから「追加」「削除」「変更」機能を追加しました。
これで一通り、「toDoアプリの機能」は実装できましたね!
デザインはあなたで考えてみて下さい!
あとは、どのように機能を増やして、UIを変えて使いやすくするかは、読者の皆様次第です。
例えば、管理者ページのカレンダーで、前後2年分のカレンダーが表示できるようにするのも良いですし、今日の日付を強調したりするのも良いかもしれません。
また、アプリケーションのランディングページ(トップページ)が質素すぎるので、もっと「ユーザの心を掴むようなデザイン」にするのも必要そうです。
本連載では、その部分はやりません。
そのため、余力がある人はぜひチャレンジしてみてくださいね!
なお、FastAPIについての開発依頼・お見積もりはこちらまでお願いします。
次回は最終回!
ちなみに、本連載は次回で最終回となります!
最後は、JSONレスポンスを取得できるようにして、WebAPIとしても機能するようにしていきます!
最終回の記事はこちら
ResponderとFastAPIを実際に使って比較してみた
こちらの記事もオススメ!
書いた人はこんな人

- 「好きを仕事にするエンジニア集団」の(株)ライトコードです!
ライトコードは、福岡、東京、大阪の3拠点で事業展開するIT企業です。
現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。
いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。
システム開発依頼・お見積もり大歓迎!
また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」「WEBディレクター」を積極採用中です!
インターンや新卒採用も行っております。
以下よりご応募をお待ちしております!
https://rightcode.co.jp/recruit
ITエンタメ10月 13, 2023Netflixの成功はレコメンドエンジン?
ライトコードの日常8月 30, 2023退職者の最終出社日に密着してみた!
ITエンタメ8月 3, 2023世界初の量産型ポータブルコンピュータを開発したのに倒産!?アダム・オズボーン
ITエンタメ7月 14, 2023【クリス・ワンストラス】GitHubが出来るまでとソフトウェアの未来