対象
- PythonとFlaskの基礎はなんとなく学習したから、何か成果物を作ってみたい
- ただの掲示板ではなく、何か機能を付け加えた掲示板を作ってみたい
実装環境
- Windows 10 Home 64bit
- Python 3.9.6
ライブラリ
- Flask==2.0.2
- Flask-SQLAlchemy==2.5.1
- Jinja2==3.0.3
- SQLAlchemy==1.4.35
- sqlite3
ディレクトリ構成
flask_bbs
┝ app.py
┝ templates
│ ┝ index.html
│ ┝ layout.html
│ ┝ result.html
│ ┝ delete.html
│ ┝ error.html
app.pyの作成
必要なライブラリのインポート
import re
from flask import Flask, request, render_template
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import validates
app = Flask(__name__)
SQLALCHEMY_TRACK_MODIFICATIONS = False
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# SQLAlchemyでデータベースに接続する
db_uri = 'sqlite:///test.db'
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
db = SQLAlchemy(app)
しりとり機能を持たせるために、正規表現ライブラリreをインポートします。
また、validationが必要になるので、sqlalchemy.ormからvalidatesを利用しましょう。
テーブルの作成
class Comment(db.Model):
"""[テーブルの定義を行うクラス]
Arguments:
db {[Class]} -- [ライブラリで用意されているクラス]
"""
__tablename__ = 'Comment'
id_ = db.Column(db.Integer, primary_key=True, autoincrement=True)
pub_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
name = db.Column(db.Text())
comment = db.Column(db.Text())
def __init__(self, pub_date, name, comment):
"""[テーブルの各カラムを定義する]
[Argument]
id_ -- 投稿番号(プライマリキーなので、自動で挿入される)
pub_date -- 投稿日時
name -- 投稿者名
comment -- 投稿内容
"""
self.pub_date = pub_date
self.name = name
self.comment = comment
@validates('name')
def validate_name(self, key, name):
if len(name) <= 1:
raise ValueError("名前を2文字以上で入力してください")
name_start = re.compile(r'^[a-zA-Z].*').search(name)
name_end = re.compile(r'.*[a-zA-Z]$').search(name)
if name_start == None or name_end == None:
raise ValueError("名前の先頭と末尾は半角アルファベットで入力してください")
last_name = Comment.query.order_by(Comment.id_.desc()).first()
if last_name != None:
if str(name[0]) != str(last_name.name)[-1]:
raise ValueError("前の投稿の名前の末尾の文字=名前の先頭文字になるよう入力してください")
return name
@validates('comment')
def validate_comment(self, key, comment):
if len(comment) <= 1:
raise ValueError("投稿内容を2文字以上で入力してください")
comment_start = re.compile(r'^[あ-わ].*').search(comment)
comment_end = re.compile(r'.*[あ-わ]$').search(comment)
if comment_start == None or comment_end == None:
raise ValueError("投稿内容の先頭と末尾は、「ん」と小文字以外のひらがなで入力してください")
last_comment = Comment.query.order_by(Comment.id_.desc()).first()
if last_comment != None:
if str(comment[0]) != str(last_comment.comment)[-1]:
raise ValueError("前の投稿内容の末尾の文字=投稿内容の先頭文字になるよう入力してください")
return comment
try:
db.create_all()
except Exception as e:
print(e.args)
pass
名前は2文字以上の半角アルファベット、投稿内容は2文字以上の全角ひらがなという制限をつけました(正確には先頭と末尾のみ制限をつけています)。
また、前回の投稿を参照するために、降順に並べたときの最初の一行を
Comment.query.order_by(Comment.id_.desc()).first()で取り出しています。
そこにif last_name(last_comment) != None:の条件をつけることで、最初の投稿にはしりとりの制限をつけないようにしています。
ルーティングの設定
@app.route("/")
def index():
# テーブルから投稿データをSELECT文で引っ張ってくる
text = Comment.query.all()
return render_template("index.html", lines=text)
@app.route("/result", methods=["POST"])
def result():
# 現在時刻 投稿者名 投稿内容を取得
date = datetime.now()
name = request.form["name"]
comment = request.form["comment_data"]
# テーブルに格納するデータを定義する
comment_data = Comment(pub_date=date, name=name, comment=comment)
# テーブルにINSERTする
db.session.add(comment_data)
# テーブルへの変更内容を保存
db.session.commit()
return render_template("result.html", comment=comment, name=name, now=date)
@app.route('/delete', methods=["POST"])
def all_delete():
DleteDataset = db.session.query(Comment).all()
for DleteData in DleteDataset:
db.session.delete(DleteData)
db.session.commit()
date = datetime.now()
comment_ = 'しりとり'
name_ = 'start'
start_data = Comment(pub_date=date, name=name_, comment=comment_)
db.session.add(start_data)
db.session.commit()
return render_template('delete.html')
@app.errorhandler(500)
def valueerror(error):
return render_template('error.html'), 500
if __name__ == "__main__":
# app.run(debug=True)にpython app.pyではなく、flask run --debugger --reload で起動すると、自動リロードが走る
app.run(debug=True)
4つのhtmlファイルを用意し、ルートを振り分けています。
ValueErrorはerrorhandlerを使い、error.htmlを表示するよう設定しています。
deleteを実行すると、名前がstart、投稿内容がしりとりの投稿が自動で生成されるようにしています。
このapp.pyを実行する際は、python app.pyではなく flask run --debugger --reloadで起動すると、ファイルの編集等がしやすいと思います。
htmlファイルの用意
{% extends 'layout.html' %}
{% block content %}
<h1>しりとり掲示板へようこそ!</h1>
<form action="/result" method="post">
<input type="hidden" name="comment_box">
<label for="comment_">投稿</label>
<textarea name="comment_data" rows="6" cols="100"></textarea>
<p></p>
<label for="name">名前</label>
<input type="text" name="name">
<button type="submit">送信する</button>
</form>
<h2>投稿一覧</h2>
<form method="post" action="">
<table class="table">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>日付</th>
<th>名前</th>
<th>投稿内容</th>
</tr>
</thead>
<tbody>
{% for th in lines | reverse %}
<tr>
<td>{{ th.id_ }}</td>
<td>{{ th.pub_date }}</td>
<td>{{ th.name }}</td>
<td>{{ th.comment }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
<p></p>
<form action="/delete" method="post">
<input type="hidden" name="comment_box">
<label for="comment_">データ削除ボタン</label>
<p></p>
<button type="submit">データを全て削除する</button>
</form>
{% endblock %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>しりとり掲示板</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<!-- <style>body {padding: 10px;}</style> -->
</head>
<body>
{% block content %}
{% endblock %}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>
{% extends "layout.html" %}
{% block content %}
<h1>コメントを書き込みました</h1>
<br>
<form action="/" method="get">
<button type="submit">戻る</button>
</form>
{% endblock %}
{% extends "layout.html" %}
{% block content %}
<h1>コメントを全て削除しました</h1>
<br>
<form action="/" method="get">
<button type="submit">戻る</button>
</form>
{% endblock %}
{% extends "layout.html" %}
{% block content %}
<h1>入力エラー</h1>
<h2>入力ルールをもう一度ご確認ください</h2>
<br>
<form action="/" method="get">
<button type="submit">戻る</button>
</form>
{% endblock %}
実際に作成するときは、cp templates/result.html templates/delete.html などのコマンドを用いて、コピーして作成すると効率が上がります。
実際に遊んでみた
感想
しりとり機能を付け加えるのは、そこまで難しくないことがわかりました。
validatesを使いこなせば、他にも色々な制限を設けることが簡単にできます。
ぜひvalidationで遊んでみてください。