この記事では、FlaskとSQLiteを用いた、基本的なCRUD処理の勉強もかねて、書籍管理機能の実装をします。
また、Bootstrapを使って、比較的簡単に、それなりにきれいな画面を作っていきます。

この記事で、書籍管理アプリのすべての機能の実装が終わります。
コード全体の概要は『Visual StudioとFlaskで作る書籍管理アプリ:外枠を作る』を参照してください。
CRUD処理の対象となるbookテーブルについては『SQLiteとDB Browser for SQLiteを使ってデータベースを構築する』を参照してください。

この記事はVisual StudioとFlaskで書籍管理アプリを作ってみたという一連の記事の中の1つです。
詳しくは以下の記事も参照してください
Visual Studioで作ってみたFlaskアプリ
Visual StudioとFlaskで作る書籍管理アプリ:概要

この記事ではWebアプリの開発を何も知らない人を対象とします。といっても、この記事の著者もWebアプリ開発に明るくありません。自分の勉強の意味も込めて書きました。
誤りが含まれるかもしれませんが、ご容赦ください。何かあれば、ご指摘いただけますと幸いです。



スポンサードリンク

目次

  1. 書籍一覧の取得処理の実装
  2. 書籍一覧画面(テンプレート)の作成
  3. 書籍登録処理の実装
  4. 書籍更新処理の実装
  5. 書籍削除処理の実装
  6. 書籍の存在チェックを行う関数の実装
  7. 書籍の登録・更新・削除確認画面の作成
  8. 動作確認

 

1.書籍一覧の取得処理の実装

書籍一覧の取得処理から実装していきます。
画面への表示の方法は少し工夫がいりますが、データを取得するだけならば、それほど複雑ではありません。

以下のようになります。

@bp.route('/')
@login_required
def index_book():
    """書籍の一覧を取得する"""

    # DBと接続
    db = get_db()

    # 書籍データを取得
    user_id = session.get('user_id')
    books = db.execute(
        'SELECT * FROM book WHERE user_id = ? ORDER BY id DESC', (user_id,)
    ).fetchall()

    # 書籍一覧画面へ遷移
    return render_template('book/index_book.html',
                           books=books,
                           title='ログイン',
                           year=datetime.now().year)

今回は、ユーザーに紐づく書籍だけを取得していることに注意してください。
セッションからログインしているユーザーのユーザーIDを取得したうえで、それをSELECT文のWHERE句で使用しています。

 

2.書籍一覧画面(テンプレート)の作成

書籍一覧画面のテンプレートを先に作成しておきます。

Visual Studioのソリューションエクスプローラーの「templatesフォルダ」を右クリックして『追加』→『新しいフォルダ』として「book」という名前のフォルダを新規追加します。
bookフォルダを右クリックして『追加』→『新しい項目』→『HTMLファイル』と選択して「index_book.html」という名前で保存します。

index_book.htmlは以下のように実装します。

{% extends "layout.html" %}
{% block content %}

    <h1 style="margin-bottom:50px">書籍一覧</h1>

    {% for category, message in get_flashed_messages(with_categories=true) %}
    <div class="{{ category }}">{{ message }}</div>
    {% endfor %}

    <a class="h3" href="{{ url_for('book.create_book') }}">書籍の新規追加</a>

    <table class="table table-hover">
        <thead>
            <tr>
                <th>タイトル</th>
                <th>著者・作者</th>
                <th>出版社</th>
                <th>編集</th>
            </tr>
        </thead>
        <tbody>
            {% for book in books %}
            <tr>
                <td>{{ book.title }}</td>
                <td>{{ book.auther }}</td>
                <td>{{ book.publisher }}</td>
                <td>
                    <a href="{{ url_for('book.update_book', book_id=book.id) }}">編集</a>|
                    <a href="{{ url_for('book.delete_book', book_id=book.id) }}">削除</a>
                </td>
            </tr>
            {% else %}
            <td>書籍が登録されていません</td>
            {% endfor %}
        </tbody>
    </table>

{% endblock %}

エラーや処理の結果など、何かメッセージを表示するときのために、6~8行目で、「flash」で送られてきたメッセージがあればそれを表示するようにしています。

10行目は書籍の新規追加画面へのリンクです。『url_for』を使うことでURLをべた書きしなくても遷移先を指定できます。

12行目からが、書籍一覧を表示する部分です。テーブルレイアウトを適用するのですが、Bootstrapを使っているので『class=”table table-hover”』と指定するだけで、マウスを載せると色が変わる、ちょっとおしゃれ(?)な表を簡単に作成することができます。
表題を設定した後の22行目からが重要です。ここで、先ほど作った『index_book()』関数から送られてきた『books』を、ループして、1行ずつ表示させています。
また、編集・削除画面へのリンクも併せて入れ込むようにしてあります。『url_for(‘book.update_book’, book_id=book.id)』のようにすることで、例えば書籍ID等任意の引数を設定してやることができます。

書籍が1冊も登録されていなかった時の処理が32~34行目です。「書籍が登録されていません」と表示させておきます。



スポンサードリンク

 

3.書籍登録処理の実装

書籍の登録処理を実装します。
Visual StudioとFlaskで作る書籍管理アプリ:ログイン機能の実装』で実装したユーザー登録処理とほぼ同様です。
処理の意味はコメントとして入れておきました。

@bp.route('/create_book', methods=('GET', 'POST'))
@login_required
def create_book():
    """
    GET :書籍登録画面に遷移
    POST:書籍登録処理を実施
    """
    if request.method == 'GET':
        # 書籍登録画面に遷移
        return render_template('book/create_book.html',
                               title='書籍の追加',
                               year=datetime.now().year)


    # 書籍登録処理

    # ユーザーIDを取得
    user_id = session.get('user_id')

    # 登録フォームから送られてきた値を取得
    title = request.form['title']
    auther = request.form['auther']
    publisher = request.form['publisher']

    # DBと接続
    db = get_db()

    # エラーチェック
    error_message = None

    if not title:
        error_message = '書籍タイトルの入力は必須です'

    if error_message is not None:
        # エラーがあれば、それを画面に表示させる
        flash(error_message, category='alert alert-danger')
        return redirect(url_for('book.create_book'))

    # エラーがなければテーブルに登録する
    db.execute(
        'INSERT INTO book (user_id, title, auther, publisher) VALUES (?, ?, ?, ?)',
        (user_id, title, auther, publisher)
    )
    db.commit()

    # 書籍一覧画面へ遷移
    flash('書籍が追加されました', category='alert alert-info')
    return redirect(url_for('book.index_book'))

 

4.書籍更新処理の実装

続いて書籍更新処理です。
追加処理と異なるのは、
1.書籍IDをURLから受け取る点→存在しない書籍IDが対象となっていたら、エラーにする
2.書籍更新画面に対して、書籍情報を送らなくてはならない点
3.SQLがINSERTからUPDATEに変わった点
となります。それ以外は大体同じです。

@bp.route('/<int:book_id>/update_book', methods=('GET', 'POST'))
@login_required
def update_book(book_id):
    """
    GET :書籍更新画面に遷移
    POST:書籍更新処理を実施
    """

    # 書籍データの取得と存在チェック
    book = get_book_and_check(book_id)

    if request.method == 'GET':
        # 書籍更新画面に遷移
        return render_template('book/update_book.html',
                               book=book,
                               title='書籍の編集',
                               year=datetime.now().year)

    # 書籍編集処理

    # 登録フォームから送られてきた値を取得
    title = request.form['title']
    auther = request.form['auther']
    publisher = request.form['publisher']

    # DBと接続
    db = get_db()

    # エラーチェック
    error_message = None

    if not title:
        error_message = '書籍タイトルの入力は必須です'

    if error_message is not None:
        # エラーがあれば、それを画面に表示させる
        flash(error_message, category='alert alert-danger')
        return redirect(url_for('book.update_book'))


    # エラーがなければテーブルに登録する
    db.execute(
        'UPDATE book SET title=?, auther=?, publisher=? WHERE id=?',
        (title, auther, publisher, book_id)
    )
    db.commit()

    # 書籍一覧画面へ遷移
    flash('書籍が編集されました', category='alert alert-info')
    return redirect(url_for('book.index_book'))

書籍データの存在チェックを行う『get_book_and_check(book_id)』という関数は、後ほど実装します。
書籍の存在チェック機能は書籍削除でも用いますので、流用できるように、切り出しました。

 

5.書籍削除処理の実装

書籍削除処理です。
追加や登録と違って、書籍の情報はIDだけで済むので、気楽です。
うっかり削除されてしまうのを防ぐために、いったん「削除確認画面」を介してから、削除を実行するようにしています。

@bp.route('/<int:book_id>/delete_book', methods=('GET', 'POST'))
@login_required
def delete_book(book_id):
    """
    GET :書籍削除確認画面に遷移
    POST:書籍削除処理を実施
    """
    # 書籍データの取得と存在チェック
    book = get_book_and_check(book_id)

    if request.method == 'GET':
        # 書籍削除確認画面に遷移
        return render_template('book/delete_book.html',
                               book=book,
                               title='書籍の削除',
                               year=datetime.now().year)

    # 書籍の削除処理

    db = get_db()
    db.execute('DELETE FROM book WHERE id = ?', (book_id,))
    db.commit()

    # 書籍一覧画面へ遷移
    flash('書籍が削除されました', category='alert alert-info')
    return redirect(url_for('book.index_book'))

 

6.書籍の存在チェックを行う関数の実装

書籍の存在チェックを行う関数を実装します。
書籍IDを使って書籍データをSELECTして、対象の有無を判定します。
もしも対象がなければエラーにします。

def get_book_and_check(book_id):
    """書籍の取得と存在チェックのための関数"""

    # 書籍データの取得
    db = get_db()
    book = db.execute(
        'SELECT * FROM book WHERE id = ? ', (book_id,)
    ).fetchone()

    if book is None:
        abort(404, 'There is no such book!!')

    return book

 

7.書籍の登録・更新・削除確認画面の作成

書籍の登録画面などを作成します。
ソリューションエクスプローラーからbookフォルダを右クリックして『追加』→『新しい項目』→『HTMLファイル』と選択して、以下のファイルを作成します。
・create_book.html
・update_book.html
・delete_book.html

 
書籍の登録画面から作成します。
create_book.htmlのコードです。

{% extends "layout.html" %}

{% block content %}

    <h1 style="margin-bottom:50px">書籍の追加</h1>

    {% for category, message in get_flashed_messages(with_categories=true) %}
    <div class="{{ category }}">{{ message }}</div>
    {% endfor %}

    <form method="post">
        <div class="form-group">
            <label>本のタイトル</label>
            <input type="text" class="form-control" name="title">
        </div>
        <div class="form-group">
            <label>著者</label>
            <input type="text" class="form-control" name="auther">
        </div>
        <div class="form-group">
            <label>出版社</label>
            <input type="text" class="form-control" name="publisher">
        </div>
        <button type="submit" class="btn btn-primary">新規追加</button>
    </form>

{% endblock %}

 
書籍の更新画面です。bookデータを受け取って、フォームにあらかじめ設定しておくという点が、新規登録画面と異なります。あとは大体同じです。
update_book.htmlのコードです。

{% extends "layout.html" %}

{% block content %}

    <h1 style="margin-bottom:50px">書籍の編集</h1>

    {% for category, message in get_flashed_messages(with_categories=true) %}
    <div class="{{ category }}">{{ message }}</div>
    {% endfor %}

    <form method="post">
        <div class="form-group">
            <label>本のタイトル</label>
            <input type="text" class="form-control" name="title" value="{{ book.title }}">
        </div>
        <div class="form-group">
            <label>著者</label>
            <input type="text" class="form-control" name="auther"value="{{ book.auther }}">
        </div>
        <div class="form-group">
            <label>出版社</label>
            <input type="text" class="form-control" name="publisher" value="{{ book.publisher }}">
        </div>
        <button type="submit" class="btn btn-primary">更新</button>
    </form>

{% endblock %}

 
最後は書籍の削除画面です。「本当に削除しますか」と確認をするだけです。気が変わった時のことを考えて、「戻る」リンクを追加しておきました。
delete_book.htmlのコードです。


{% extends "layout.html" %}

{% block content %}

    <h1 style="margin-bottom:50px">書籍の削除の確認</h1>

    <p>{{ book.title }}を本当に削除しますか?(<a href={{url_for('book.index_book')}}>戻る</a>)</p>

    <form method="post">
        <button type="submit" class="btn btn-primary">書籍の削除</button>
    </form>

{% endblock %}

これで書籍管理アプリのすべての機能が実装されました。

誤りなどあれば、ご指摘いただけると幸いです。

 

8.動作確認

せっかく作ったので、動かしてみます。Visual StudioでF5キーを押すとアプリが起動されます。

testさんとしてログインします。

 
「書籍一覧へ」ボタンを押して、書籍一覧画面に遷移します。

 
「書籍の新規追加」のリンクを押して、書籍情報を登録していきます。

 
3冊ほど書籍を登録してみました。
しかし、「書籍無量大数」の出版社名が「思いつかなかった」などという問題のある内容になっていますね。

 
「書籍無量大数」において、編集のリンクを押して、出版社名を「誤りがあったので修正します」に変更しました。

 
無事に編集ができたようです。

 
「書籍無量大数」はやっぱりいらないので削除します。

 
正しく削除ができました。これでCRUD処理はOKですね。

 
最後に、存在しない書籍IDを対象にして、更新画面のURLをたたいてみます。
すると、エラーメッセージが表示されます。

これでとりあえずの機能は完成しました。

 

関連する記事

 

参考文献

Welcome to Flask
→Flaskの全体像をつかむことができる公式サイトです。ここのチュートリアルを参考にしました。


独学プログラマー Python言語の基本から仕事のやり方まで

 
Pythonのことがさっぱりわからないという方は、この本から入ると良いかと思います。あまり分厚くない本ですので、読み切ることは難しくないはずです。
Pythonの基本文法やコードを書くコツなどが載っています。
 

入門Python3

 
こちらは分厚い本ですが、その分細かく載っています。Flaskの解説も少しあります。辞書的な使い方をしても良い本かと思います。
 

演習で力がつく HTML/CSSコーディングの教科書

 
HTMLとCSSはこの本を読んで勉強しました。
Web開発の書籍はたくさんあるので、お好きなものを選ばれたら良いかなと思います。
 



スポンサードリンク

 
更新履歴
2018年5月26日:新規作成