【Flask】電子掲示板を作る~⑤投稿作成ページを機能させる~
スポンサードリンク
おはようございます。Shotaです。
今日は一昨日に引き続き、PythonのFlaskを用いた電子掲示板の作り方について解説していきます。
今日の記事では投稿を作るページを機能させる方法を徹底解説します。
投稿作成ページを機能させる
一昨日の記事では、Pythonファイル「main.py」とhtmlファイル「create.html」を下の部分まで仕上げました。
▼main.py
from flask import Flask, render_template from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///postdata.db' db = SQLAlchemy(app) class Post(db.Model): id = db.Column(db.Integer, primary_key=True) user = db.Column(db.String(20), nullable=False) title = db.Column(db.String(30), nullable=False) detail = db.Column(db.Text, nullable=False) post_date = db.Column(db.DateTime, nullable=False) @app.route('/') def index(): return render_template('index.html') @app.route('/create') def create(): return render_template('create.html') if __name__ == '__main__': app.run(debug=True)
▼create.html
{% extends 'base.html' %} {% block body %} <h1>新規作成</h1> <div class="container"> <form action="/" method="POST"> <p>日付:<input type="date" name="post_date"></p> <input class="form-control" name="user" type="text" placeholder="名前(全角20文字以内)" aria-label="default input example"> <input class="form-control" name="title" type="text" placeholder="タイトル(全角30文字以内)" aria-label="default input example"> <div class="mb-3"> <label for="exampleFormControlTextarea1" class="form-label">投稿内容</label> <textarea class="form-control" name="detail" id="exampleFormControlTextarea1" rows="3"></textarea> </div> <button href="/" class="btn btn-outline-success">戻る</button> <button type="submit" class="btn btn-success">投稿する</button> </form> </div> {% endblock %}
Pythonファイルを実行してサーバを立ち上げ、/create
ページにアクセスすると以下のように表示させることができます。
しかし、上記のファイルのままでは投稿作成ページを機能させることはできません。(データを入力して「投稿する」ボタンを押すと、下のようなエラーが表示されます)
フォームのアクセス先の設定が原因でこのような結果になります。
というのも、フォーム送信の宛先はaction="/"
でトップページになっていますが、そのトップページではPOSTを受けられる状態になっていません。
言い換えれば、GETしか受け取れない状態になっています。
そのためには、Pythonファイルを編集して機能させる必要があります。
そこで、トップページにルーティングしている関数のデコレータ部分を、少し変更してあげましょう。
@app.route('/') #このコードを @app.route('/', methods=['GET', 'POST']) #このように変更する
上記のように書き換えることで、トップページでPOSTメソッドを受け入れる準備ができました。なので、もう一度/create
にアクセスして、投稿を入力して送信してみます。
そうすると、元の画面に戻ってこれるはずです。
少し現状を整理してみましょう。
/create
ページにアクセスして、投稿内容を作成することができた- タスク作成後は、トップページに遷移できるようになった
- しかし、入力したタスクを保存、および表示できていない
したがって、あとはトップページのコードを変更してあげて、
①データベースにタスクを保存
②保存されているタスクを表示する
以上の機能を実装すると良さそうですね。
投稿内容の保存・表示
Pythonファイルの操作
①データベースにタスクを保存
②保存されているタスクを表示する
これらをトップページで実装していきます。そのためには、以下のコードをいじっていきます。
@app.route('/', methods=['GET', 'POST']) def index(): return render_template('index.html')
追加のインポート
まずは、追加で以下のコードをimportしましょう。
追加するのはrequest
とredirect
の2つのクラスです。
from flask import Flask, render_template #これだったけど from flask import Flask, render_template, request, redirect #こちらに変更
以下の挙動をプログラムに付け加えていこうと思います。
- POSTメソッド:データベースにタスクを保存する
- GETメソッド:保存されているタスクを表示
このように、リクエスト方法に応じて実装内容を変更していく際にrequest
が必要になります。
またredirect
は、POSTで受け取った内容をデータベースに反映した後に、再度トップページにアクセスするために使います。
投稿内容の保存と表示をするコードを書いていきましょう。以下のようにmain.pyを編集してあげます。
from datetime import datetime # 中略 @app.route('/', methods=['GET', 'POST']) def index(): if request.method == 'GET': posts = Post.query.all() return render_template('index.html', posts=posts) else: user = request.form.get('user') title = request.form.get('title') detail = request.form.get('detail') post_date = request.form.get('post_date') post_date = datetime.strptime(post_date, '%Y-%m-%d') new_post = Post(user=user, title=title, detail=detail, post_date=post_date) db.session.add(new_post) db.session.commit() return redirect('/')
GETとPOSTに分解して考えます。
GET―タスクの表示部分
if request.method == 'GET': posts = Post.query.all() return render_template('index.html', posts=posts)
この部分でやっていることは、「データベースからすべての投稿を取り出し、それをトップページに渡す」ことです。
POST―タスクの保存部分
else: user = request.form.get('user') title = request.form.get('title') detail = request.form.get('detail') post_date = request.form.get('post_date') post_date = datetime.strptime(post_date, '%Y-%m-%d') new_post = Post(user=user, title=title, detail=detail, post_date=post_date) db.session.add(new_post) db.session.commit() return redirect('/')
もしリクエストがPOSTなら、データベースに投稿を保存します。
上記でやっていることは主に3つです。
- POSTされた内容を受け取る
Post
クラスに受け取った内容を渡す- データベースに投稿を保存する
上述がデータベースへ投稿するまでのステップです。
データベースへ保存するときは、まずadd
で内容を追加して、実際に反映するためにcommit
します。
post_date = datetime.strptime(post_date, '%Y-%m-%d')
また、途中で変数post_date
をキャスト(Pythonにおける、データの型変換)している部分があります。これは、データベースの定義でフォームから受け取る値が文字列だからです。よって、Python上で文字列から日付型にキャストしています。
これでPython側でやることは完了しましたので、あとは投稿内容を反映するためにhtmlを編集するだけです。
HTMLファイルの操作
Bootstrapにアクセスして、index.html
を編集します。
「card」と検索して、以下のカードを使います。
そして、次のコードをindex.html
にコピペしてください。
<div class="card"> <h5 class="card-header">Featured</h5> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div>
index.html
の全体像は次の通りです。
{% extends 'base.html' %} {% block body %} <h1>Index Page</h1> <div class="card"> <h5 class="card-header">Featured</h5> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> {% endblock %}
これでPythonファイルを実行してサーバを立ち上げると、次のように画面が表示されるかと思います。
ただし、これだと見栄えが良いとは言えないのでcard
クラスが付いてあるdiv
タグの上に次のコードを載せてください。
<div class="container"></div>
完成形は次のようになります。
<div class="container"> <div class="card"> <h5 class="card-header">Featured</h5> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> </div>
これで再度サーバを立ち上げてみてください。
このように形が整うと思います。あとは、次のような機能をindex.html
上に実装していきます。
- 「投稿する」ボタンを作る(同時にページに遷移させる)
- カードに日付と投稿した日時、タイトルを記す
- 「詳細」「削除」ボタンを付ける
まずは、投稿するボタンを作りましょう。再度Bootstrapにアクセスして、「button」と検索します。ボタンは次のようなものを使っていきます。アレンジ等はご自由にやってもらって大丈夫です。
▼使うボタンのソース
「投稿する」ボタンのコードは、次のようなものを使います。
<button type="button" class="btn btn-info">Info</button>
これを、投稿ページに遷移できるように以下のようにアレンジします。
<button href="/create" type="button" class="btn-lg btn-info">投稿する</button>
これをindex.html
にコピペしてください。index.html
の完成形は次のようになります。
{% extends 'base.html' %} {% block body %} <h1>Index Page</h1> <button href="/create" class="btn-lg btn-info">投稿する</button> <div class="container"> <div class="card"> <h5 class="card-header">Featured</h5> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div> </div> {% endblock %}
コードが整ったら、Pythonファイルを実行してindex.html
を確認してみましょう。そうすると、以下のような画面が表示されると思います。
今度は「詳細」ボタンと「削除」ボタンを付けていきます。以下のコードをコピペしてください。
▼「詳細」ボタン
<button type="button" class="btn btn-secondary">Secondary</button>
▼「削除」ボタン
<button type="button" class="btn btn-danger">Danger</button>
これらをアレンジして、index.html
に加えると、次のようになります。
{% extends 'base.html' %} {% block body %} <h1>Index Page</h1> <button href="/create" class="btn-lg btn-info">投稿する</button> <div class="container"> <div class="card"> <h5 class="card-header">Featured</h5> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <button type="button" href="/detail" class="btn-lg btn-secondary">詳細</button> <button type="button" href="/delete" class="btn-lg btn-danger">削除</button> </div> </div> </div> {% endblock %}
これでPythonファイルを実行してサーバを立ち上げてください。そうなると、次のような画面が表示されます。
あとは、カードに投稿する内容を表示するだけです。
index.html
を次のように編集しましょう。
{% extends 'base.html' %} {% block body %} <h1>Index Page</h1> <a href="/create" class="btn-lg btn-info" role="button">投稿する</a> <div class="container"> {% for post in posts %} <div class="card"> <h5 class="card-header">日付:{{ post.post_date.date() }}</h5> <div class="card-body"> <h5 class="card-title">タイトル:{{ post.title }}</h5> <p class="card-text">投稿者:{{ post.user }}</p> <a role="button" href="/detail" class="btn-lg btn-secondary">詳細</a> <a role="button" href="/delete" class="btn-lg btn-danger">削除</a> </div> </div> {% endfor %} </div> {% endblock %}
このように書くことで、投稿した内容を表示できます。
{% for post in posts %}{% endfor %}
で、先程Pythonから渡しておいた投稿たちをfor loopしています。
さらに、ループしたpost
に入っているタイトルをpost.title
、投稿者をpost.user
、日付をpost.post_date.date()
のように書けば中身を取り出せます。
なお、表示したい変数部分は{}を二重重ねで囲むのが原則です。
これで、サーバを立ち上げて/create
にアクセスして投稿を作成して「投稿する」ボタンを押してみます。
これで、トップページに遷移しつつ投稿内容を表示されるようになりました。
さらに、投稿を追加すれば2つも表示されるようになります。
これで十分アプリケーションに近づいたと思います。
投稿の作成に加えて、あとは次のような実装を加えていきます。
- 投稿内容を確認する
- 投稿を削除する
これ以上説明すると記事が長くなってしまいますので、今回の記事はここまでにしておきます。
まとめ
今日の記事では主に次のことについて解説しました。
今日使ったファイルとそのソースコードを次に示しておきます。
▼main.py
from flask import Flask, render_template, request, redirect from flask_sqlalchemy import SQLAlchemy from datetime import datetime, date app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///postdata.db' db = SQLAlchemy(app) class Post(db.Model): id = db.Column(db.Integer, primary_key=True) user = db.Column(db.String(20), nullable=False) title = db.Column(db.String(30), nullable=False) detail = db.Column(db.Text, nullable=False) post_date = db.Column(db.DateTime, nullable=False) @app.route('/', methods=['GET', 'POST']) def index(): if request.method == 'GET': posts = Post.query.all() return render_template('index.html', posts=posts) else: user = request.form.get('user') title = request.form.get('title') detail = request.form.get('detail') post_date = request.form.get('post_date') post_date = datetime.strptime(post_date, '%Y-%m-%d') new_post = Post(user=user, title=title, detail=detail, post_date=post_date) db.session.add(new_post) db.session.commit() return redirect('/') @app.route('/create') def create(): return render_template('create.html') if __name__ == '__main__': app.run(debug=True)
▼index.html
{% extends 'base.html' %} {% block body %} <h1>Index Page</h1> <a href="/create" class="btn-lg btn-info" role="button">投稿する</a> <div class="container"> {% for post in posts %} <div class="card"> <h5 class="card-header">日付:{{ post.post_date.date() }}</h5> <div class="card-body"> <h5 class="card-title">タイトル:{{ post.title }}</h5> <p class="card-text">投稿者:{{ post.user }}</p> <a role="button" href="/detail" class="btn-lg btn-secondary">詳細</a> <a role="button" href="/delete" class="btn-lg btn-danger">削除</a> </div> </div> {% endfor %} </div> {% endblock %}
▼create.html
{% extends 'base.html' %} {% block body %} <h1>新規作成</h1> <div class="container"> <form action="/" method="POST"> <p>日付:<input type="date" name="post_date"></p> <input class="form-control" name="user" type="text" placeholder="名前(全角20文字以内)" aria-label="default input example"> <input class="form-control" name="title" type="text" placeholder="タイトル(全角30文字以内)" aria-label="default input example"> <div class="mb-3"> <label for="exampleFormControlTextarea1" class="form-label">投稿内容</label> <textarea class="form-control" name="detail" id="exampleFormControlTextarea1" rows="3"></textarea> </div> <a href="/" class="btn btn-outline-success">戻る</a> <button type="submit" class="btn btn-success">投稿する</button> </form> </div> {% endblock %}
明日の記事では、投稿内容の詳細を表示するプログラムを書いていきます。
長くなりましたが、今日の記事はこれで終了です。
開発環境
【参考サイト】