はじめに
こんにちは。
アドベントカレンダー2019のPythonカテゴリのエントリです。
2018年年末からPythonを趣味で書き始めた、異業種、実務未経験の者です。
今日は、Flaskを使って、モデル、ビューを書いてみて、色々苦労したのでそれを書きたいと思います。
特に、削除はめちゃめちゃ苦労しました。
色々おかしなところがあるかも知れませんが、一応機能しました。
初心者向けとなります。
完成アプリ
完成コード
from flask import Flask,render_template,request,redirect
app = Flask(__name__)
class DatabaseInit():
db = []
def __init__(self):
pass
class DatabaseManager(DatabaseInit):
def db_append(self,add_value):
self.db.append(add_value)
def db_get(self):
return self.db
def db_delete(self,value):
self.db.pop(value)
@app.route('/', methods=['POST','GET'])
def index():
if request.method == 'GET':
db = DatabaseInit()
return render_template('index.html')
if request.method == 'POST':
db_manage = DatabaseManager()
num = request.form.get('comment')
if num == None:
view_lists = db_manage.db_get()
else:
db_manage.db_append(num)
view_lists = db_manage.db_get()
return render_template('index.html', show_lists = view_lists)
@app.route('/delete', methods=['POST'])
def delete():
db_manage = DatabaseManager()
id = int(request.form['delete_id'])
db_manage.db_delete(id)
return redirect('/',code=307)
if __name__ == '__main__':
app.run(host='127.0.0.1',port='5000',debug=True)
<html>
<head>
<title>Flask</title>
</head>
<body>
<h1>FlaskWEBアプリ</h1>
<h2>コメント登録</h2>
<form action="/" name="comment" method="POST">
<input type="text" name="comment">
<input type="submit" name="submit_comment">
</form>
<h2>コメント表示</h2>
{% for show_list in show_lists %}
{{ show_list }}
<form method="POST" name="delete" action="/delete">
<input type="hidden" name="delete_id" value="{{ show_lists.index(show_list) }}">
<input type="submit" name="delete_id" value="削除">
</form>
</br>
{% endfor %}
</body>
</html>
書く前に考えたこと
- SQLがフルスクラッチでは書けなかったので、配列をデータベース代わりに
- データベースをモデルクラスで操作
- ファイル数を極力少なくする
環境
- Mac
- Python3.7.2
実装
仮想環境
python -m venv venv
venvを起動
source venv/bin/activate
Flaskをインストール
pip install flask
ファイルを作成
touch app.py
テンプレートを作成
Flaskはtemplatesフォルダ内にhtmlを入れる必要があります。
mkdir templates
cd templates
touch index.html
構成
/作業フォルダ
┝app.py
┗/templates
┗index.html
HelloWorld
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
if __name__ == '__main__':
app.run()
python app.py
デバッグモード、ホスト、ポートを設定
デバッグモードをTrueにしておくと、ブラウザを更新するだけで反映されるので楽です。
@app.route('/')としておくと、GET扱いになります。
つまり、ルートURLにアクセスした時に表示される画面になります。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
if __name__ == '__main__':
app.run(host='127.0.0.1',port='5000',debug=True)
htmlをレンダリング
render_templateをインポートします。
<html>
<head>
<title>Flask</title>
</head>
<body>
<h1>Hello World</h1>
<p>これはhtmlです。</p>
</body>
</html>
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(host='127.0.0.1', port='5000', debug=True)
htmlで画面を作成
action='/' は、'/'にデータを送る、ということになります。例えば、他のページに遷移する場合は、'/something'などと書けば、そのURLに対してデータを送信します。
今回はシングルページなので、今いる'/'にデータを送信します。
method="POST"は、データをPOSTで送信します。
GETはURLに公開してデータを送信、POSTは隠してデータを送信する、というようなイメージです。
今回は、GETの時に初期化されたページを表示、POSTの場合にフォームで送られたデータを更新したページを表示、します。
name='comment'は、app.pyでデータを受け取るために必要です。好きな文字列で大丈夫です。
<html>
<head>
<title>Flask</title>
</head>
<body>
<h1>FlaskWEBアプリ</h1>
<h2>コメント登録</h2>
<form action="/" name="comment" method="POST">
<input type="text" name="comment">
<input type="submit" name="submit_comment">
</form>
<h2>コメント表示</h2>
</body>
</html>
htmlにデータを渡して表示してみる
Flaskはjinja2というテンプレートエンジンを使用しています。
python側では、render_template()関数の引数に、html側の変数名=python側の変数名、を書きます。
html側では、{{ html側の変数名 }}とすると表示できます。
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
show_py = 'Pythonからきました'
return render_template('index.html',show_html = show_py)
if __name__ == '__main__':
app.run(host='127.0.0.1', port='5000', debug=True)
<html>
<head>
<title>Flask</title>
</head>
<body>
<h1>FlaskWEBアプリ</h1>
<h2>コメント登録</h2>
<form action="/" name="comment" method="POST">
<input type="text" name="comment">
<input type="submit" name="submit_comment">
</form>
<h2>コメント表示</h2>
{{ show_html }}
</body>
</html>
データベース(今回は配列)とモデルを作成
今回はデータベースを配列でやります。
DatabaseInitで配列を初期化します。
DatabaseManagerでメソッドを作り、そのメソッドを使ってデータベースを操作します。
今回は、要素を追加、要素を取得、要素を削除の3つの機能を作りました。
今回は配列を利用しましたが、SQLやDBに書き換えさえすれば、考え方は同じかと思います。
passは何も書きたくないけれど何か書く必要がある場合、書きます。要するに何もしないということです。
selfはインスタンスが代入されます。
Pythonの場合、各メソッドの第一引数は必ずselfです。
def __ init __(self):はインスタンス化された時に実行されます。
class DatabaseManager(DatabaseInit)は、継承です。リストdbにアクセスするため継承しています。
class DatabaseInit():
db = []
def __init__(self):
pass
class DatabaseManager(DatabaseInit):
def db_append(self,add_value):
self.db.append(add_value)
def db_get(self):
return self.db
def db_delete(self,value):
self.db.pop(value)
ルーティングと処理を作成
method=['GET','POST']として、GET、POST両方でアクセスできるようにします。
GETは一番最初にアクセスした時に見る画面、POSTはデータが送信された時に見る画面です。
それを、if文で処理します。
request.method==で判定します。
db = DatabaseInit()でDatabaseInitクラスをインスタンス化します。
つまり、今回はdbという空の配列を作成しています。
POSTの方の処理は、更に入れ子のif文があります。
htmlのformで文字列を送信する処理と、削除する処理を分けるためです。
num = request.form.get('comment')は、htmlのformで文字列を送信した際に代入されます。
if num == None:とありますが、つまり削除が送信された時にこちらの処理に誘導します。
文字列が送信された時に、else:以下が処理されます。
db_manage.db_append(num)で、num変数に代入されている文字列を配列に加えています。
db_manage.db_get()で配列を取得しています。
from flask import Flask,render_template,request,redirect
app = Flask(__name__)
@app.route('/', methods=['POST','GET'])
def index():
if request.method == 'GET':
db = DatabaseInit()
return render_template('index.html')
if request.method == 'POST':
db_manage = DatabaseManager()
num = request.form.get('comment')
if num == None:
view_lists = db_manage.db_get()
else:
db_manage.db_append(num)
view_lists = db_manage.db_get()
return render_template('index.html', show_lists = view_lists)
htmlでshow_listsを表示
jinja2では、{% %}で囲むことで、pythonコードを埋め込むことができます。
リストを順々に出力したいので、{% for xx in xxx %}~{% endfor %}で繰り返したい処理を囲います。
{% if 式 %}~{% endif %}だとifが書けます。
<html>
<head>
<title>Flask</title>
</head>
<body>
<h1>FlaskWEBアプリ</h1>
<h2>コメント登録</h2>
<form action="/" name="comment" method="POST">
<input type="text" name="comment">
<input type="submit" name="submit_comment">
</form>
<h2>コメント表示</h2>
{% for show_list in show_lists %}
{{ show_list }}
<form method="POST" name="delete" action="/delete">
<input type="hidden" name="delete_id" value="{{ show_lists.index(show_list) }}">
<input type="submit" name="delete_id" value="削除">
</form>
</br>
{% endfor %}
</body>
</html>
一番ハマったのが、デリート部分でした。
通常のformだと、POST、GETしか送信できない仕様なので、type="hidden"を使って、postで、name="delete_id"とvalue="{{ show_lists.index(show_list) }}"を送ります。
画面には映りません。
ちょっとformの認識が甘いので、nameの記述の必要がないところがあるかも知れません。。誰か教えてください。。
/deleteをルーティング
削除ボタンが押されたら、postで、削除する配列のインデックス値を取得し、db_delete()メソッドで配列から要素を削除します。
request.formで取得したデータは文字列なので、int()で型変換します。
redirect()は強制的にページを移動させるということです。render_template()だと、今いるURLにページを作成してしまうので、local/delete/index.htmlとか変なURLになってエラーになってしまいます。
'/'とすることで、def index():関数にルーティングされます。
code=307は、POSTでリダイレクトする、という意味です。
リダイレクトはGETでデータ送信されてしまうので、codeを307にセットすることで、POSTを維持する、という操作になります。
@app.route('/delete', methods=['POST'])
def delete():
db_manage = DatabaseManager()
id = int(request.form['delete_id'])
db_manage.db_delete(id)
return redirect('/',code=307)
以上です
初めてきちんとQiitaに技術っぽいものを書きましたので、なかなか解説が足りていない部分も多いかとは思いますが、どなたかのお役に立てれば幸いです。
間違いなどがありましたら、ご指摘いただければ修正をさせて頂きます。
今度はDBを使ってやってみたいと思います!