• トップ
  • ブログ一覧
  • 【第6回】FastAPIチュートリアル: toDoアプリを作ってみよう【予定の追加・削除編】
  • 【第6回】FastAPIチュートリアル: toDoアプリを作ってみよう【予定の追加・削除編】

    メディアチームメディアチーム
    2019.12.26

    IT技術

    FastAPIチュートリアル: toDoアプリを作ってみよう~第6回~

    前回の記事「【第5回】FastAPIチュートリアル: toDoアプリを作ってみよう【予定詳細ページ作成編】」の続きです。

    今回も、toDo アプリを作る過程で、FastAPI の使い方を学んでいきましょう!

    第1回はこちら

    featureImg2019.11.14【第1回】FastAPIチュートリアル: ToDoアプリを作ってみよう【環境構築編】FastAPIチュートリアル: toDoアプリを作ってみるFastAPI は、最近注目を集めている WebAPIフレー...

    予定の追加

    前回に引き続き、管理者ページの改良を行なっていきます。

    今回は、予定詳細ページから「予定の追加と削除」ができるようにするのが目標です。

    では、まずは「予定の追加」から行っていきたいと思います!

    ビューを改良

    まずは、予定詳細ページ(detail.html)に、新たにフォームを追加します。

    1<!--  ~~ タスク終了に関するフォーム (省略) ~~  -->
    2    {% if task | length != 0 %}
    3    <button type="submit" class="btn btn-primary">更新する</button>
    4    {% else %}
    5    <button type="submit" class="btn btn-primary" disabled="disabled">更新する</button>
    6    {% endif %}
    7</form>
    8
    9<!-- ここから追記 -->
    10<br>
    11<hr>
    12<br>
    13<h3>予定の追加</h3>
    14<form action="/add" method="post">
    15    <h5>内容</h5>
    16    <p><input type="text" size="50" maxlength="200" name="content"></p>
    17    <br>
    18    <h5>締め切り時間</h5>
    19    <p>
    20        {{ year }}年{{ month }}月{{ day }}日
    21        <select name="hour">
    22            {% for h in range(25) %}
    23            <option value="{{ h }}">{{ h }}</option>
    24            {% endfor %}
    25        </select>時
    26        <select name="minute">
    27            {% for m in range(61) %}
    28            <option value="{{ m }}">{{ m }}</option>
    29            {% endfor %}
    30        </select>分
    31    </p>
    32    <br>
    33    <button type="submit" class="btn btn-primary">予定を追加する</button>
    34</form>
    35<!-- ここまで -->
    36
    37{% endblock %}

    予定の追加は「/add」というURLに投げることにしました。

    見た目的にはこのような感じになっています。

    予定の追加フォーム

    /addをコーディング

    次に、実際に処理を書いていきましょう!

    ルーティングは、いつものごとくです。

    1# urls.py
    2# FastAPIのルーティング用関数
    3app.add_api_route('/', index)
    4app.add_api_route('/admin', admin, methods=['GET', 'POST'])
    5app.add_api_route('/register', register, methods=['GET', 'POST'])
    6app.add_api_route('/todo/{username}/{year}/{month}/{day}', detail)
    7app.add_api_route('/done', done, methods=['POST'])
    8app.add_api_route('/add', add, methods=['POST'])  # new

    コントローラも、大した処理は必要ありません。

    フォームデータをうまく処理するだけなので、以下のように書くことができます。

    1async def add(request: Request, credentials: HTTPBasicCredentials = Depends(security)):
    2    # 認証
    3    username = auth(credentials)
    4
    5    # ユーザ情報を取得
    6    user = db.session.query(User).filter(User.username == username).first()
    7
    8    # フォームからデータを取得
    9    data = await request.form()
    10    year = int(data['year'])
    11    month = int(data['month'])
    12    day = int(data['day'])
    13    hour = int(data['hour'])
    14    minute = int(data['minute'])
    15
    16    deadline = datetime(year=year, month=month, day=day,
    17                        hour=hour, minute=minute)
    18
    19    # 新しくタスクを生成しコミット
    20    task = Task(user.id, data['content'], deadline)
    21    db.session.add(task)
    22    db.session.commit()
    23    db.session.close()
    24
    25    return RedirectResponse('/admin')

    動作確認

    適当に予定を追加して...
    予定が追加されました

    予定の削除

    間違えて予定を入れてしまった場合、「予定の削除」機能が必要です。

    その機能を追加していきましょう!

    予定詳細ページに削除ボタンを追加する

    今回は、削除機能はフォームではなく、削除ボタン(リンク)で処理することにしました。

    予定一覧のテーブルに、新たな列を追加してみましょう。

    削除URLは「/delete/{task_id}」としました。

    1<h3>予定一覧</h3>
    2<p>予定を終了した場合はチェックボックスにチェックをしてください。</p>
    3<form action="/done" method="post">
    4    <table class="table">
    5        <thead class="table-dark">
    6        <tr>
    7            <th>内容</th>
    8            <th>締め切り</th>
    9            <th>終了</th>
    10            <th></th>   <!-- new -->
    11        </tr>
    12        </thead>
    13        <tbody>
    14        {% for t in task %}
    15        <tr>
    16            <td>{{ t.content }}</td>
    17            <td>{{ t.deadline }}</td>
    18            <td>
    19                {% if t.done %}
    20                <span class="text-success">終了済</span>
    21                {% else %}
    22                <input type="checkbox" name="done[]" value="{{ t.id }}"> &nbsp; <span class="text-danger">終了する</span>
    23                {% endif %}
    24            </td>
    25            <td>
    26                <a href="/delete/{{ t.id }}" class="btn btn-danger btn-sm">削除</a>   <!-- new -->
    27            </td>
    28        </tr>
    29        {% endfor %}
    30        </tbody>
    31    </table>

    見た目はこのような感じになっています。

    削除ボタンの追加

    /delete/{task_id}のコントローラを書く

    ルーティングです。

    1# FastAPIのルーティング用関数
    2app.add_api_route('/', index)
    3app.add_api_route('/admin', admin, methods=['GET', 'POST'])
    4app.add_api_route('/register', register, methods=['GET', 'POST'])
    5app.add_api_route('/todo/{username}/{year}/{month}/{day}', detail)
    6app.add_api_route('/done', done, methods=['POST'])
    7app.add_api_route('/add', add, methods=['POST'])
    8app.add_api_route('/delete/{t_id}', delete)  # new

    もう大丈夫ですね。

    そうしたらコントローラを書きますが、今回はURLパターンで削除したいタスクを取得するだけです。

    ただ、部外者が勝手にタスクを削除できないように工夫しましょう。

    1def delete(request: Request, t_id, credentials: HTTPBasicCredentials = Depends(security)):
    2    # 認証
    3    username = auth(credentials)
    4
    5    # ログインユーザ情報を取得
    6    user = db.session.query(User).filter(User.username == username).first()
    7
    8    # 該当タスクを取得
    9    task = db.session.query(Task).filter(Task.id == t_id).first()
    10
    11    # もしユーザIDが異なれば削除せずリダイレクト
    12    if task.user_id != user.id:
    13        return RedirectResponse('/admin')
    14
    15    # 削除してコミット
    16    db.session.delete(task)
    17    db.session.commit()
    18    db.session.close()
    19
    20    return RedirectResponse('/admin')

    どうでしょうか?

    ここまでくると、データベースの扱いは慣れてきたかと思います。

    SQLAlchemyでは、データベースの処理(session.query() やsession.add() 、session.delete() )の後は、session.close() する、という一連の処理を覚えておきましょう。

    データベースに変更を反映させる場合はクローズする前に、session.commit() です!

    第6回:さいごに

    今回は、予定詳細ページから「追加」「削除」「変更」機能を追加しました。

    これで一通り、「toDoアプリの機能」は実装できましたね!

    デザインはあなたで考えてみて下さい!

    あとは、どのように機能を増やして、UIを変えて使いやすくするかは、読者の皆様次第です。

    例えば、管理者ページのカレンダーで、前後2年分のカレンダーが表示できるようにするのも良いですし、今日の日付を強調したりするのも良いかもしれません。

    また、アプリケーションのランディングページ(トップページ)が質素すぎるので、もっと「ユーザの心を掴むようなデザイン」にするのも必要そうです。

    本連載では、その部分はやりません。

    そのため、余力がある人はぜひチャレンジしてみてくださいね!

    なお、FastAPIについての開発依頼・お見積もりはこちらまでお願いします。

    次回は最終回!

    ちなみに、本連載は次回で最終回となります!

    最後は、JSONレスポンスを取得できるようにして、WebAPIとしても機能するようにしていきます!

    最終回の記事はこちら

    featureImg2020.01.07【最終回】FastAPIチュートリアル: toDoアプリを作ってみよう【WebAPI編】FastAPIチュートリアル: toDoアプリを作ってみよう~最終回~前回の記事「【第6回】FastAPIチュートリア...

    ResponderとFastAPIを実際に使って比較してみた

    featureImg2020.01.10ResponderとFastAPIを実際に使って比較してみたResponderとFastAPIを比較したい!Webアプリケーションといえば、PHPの「CakePHP」、Pytho...

    こちらの記事もオススメ!

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    featureImg2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background