初心者でもわかるように
私がそうであるように、Flaskを使ってウェブアプリ開発をしたいけれども、Flaskが、websokcetの仕組みが、そもそもJavascriptでてくるなんて!え?jQuery?わからねえYOという方はいないかもしれないけど、
とにかく
- フォームでデータを送受信したい
- 更新うざい、リアルタイムでシームレスにしたい
この二つができたら、開発できるアプリのアイデアはぐっと広がると思います。
ただ、flask用のwebsocketの解説って、みーーーーーーーーーーーんなチャットのサンプルだけなんだよね。
チャットしたいわけじゃないっつーのwwww
データ受け取ることができたら、あとはPythonでゴリゴリできるのでJavascriptは極力いりませんバーロー!と心の中でつぶやきながらサンプルを探すのですが、多くのサンプルが基本機能を網羅しすぎててどこがどうなってるのかよくわからんし、__本当のファンダメンタルな基本部分を把握したい__という僕にとって苦痛以外の何物でもアリありませんでした。しかもFlaskのサンプルだけ置いて、htmlのjQuery部分は割愛とか、どうしたらいいかわっかんねーヨ!的なサイトが多かったのでここで__単純なフォームのやりとりとwebsocketによるリアルタイム通信によるフォームのやりとりを併せ持った基本的なサンプル__を作ったので置いときます。
難しい質問はしないでください。初心者なのでわからないです。
御託はここで終わりにして、以下サンプルです。
僕みたいなタコでもわかりやすく、5年後に忘れても大丈夫なレベルでメモっときます。
フォルダ構成
project
┣main.py
┣static
┃ ┗style.css
┗templates
┗index.html
┗socket.html
main.py
# coding: utf-8
from flask import Flask, request, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__) # Flask動かす時のおまじない。動けばいいわ。
app.config['SECRET_KEY'] = 'hogehugahage' # セキュリティうんぬんのおまじない。まぁオフラインの自分専用のWEBアプリなら気にしなくていいわwww
socketio = SocketIO(app, async_mode=None) # async_modeとかよくわからん。動けばいいわ。まぁおいおい調べるわ。
class SiteInfo:
title = 'ページタイトル'
# 単純なhtmlページのレンダリングとフォームの値の受け取り
@app.route("/", methods=['GET','POST']) # アドレス"/"の時にget_form()を実行しますよ、フォームのget,post両方対応しますよ
def get_form():
try: # フォームのうち一番最初のname="single"の値をテキストで受け取ります
value_single = request.form['single']
except: # 初回ロード時はフォームを受け取れないので、エラーになる
value_single = None
try: # フォームのうちname="list"の値が存在するものをリスト形式で受け取ります
value_list = request.form.getlist('list')
except: # 初回ロード時はフォームを受け取れないので、エラーになる
value_list = []
return render_template('index.html', # templetes/index.htmlの中身をレンダリングしてページに表示してくれます
title = SiteInfo.title, # index.html内にて、{{ title }}で挿入できる
value_list = value_list, # index.html内にて{{ value_list }}で挿入できる
value_single = value_single, # index.html内にて{{ value_single }}で挿入できる
)
###############ウェブソケットを使ったリアルタイム通信#################
# バックグラウンドでサーバー側から常に情報を与える
def background(comment):
num = 0
while True:
socketio.sleep(1) # time.sleepでも代用可能。たぶん
num += 1
content = "<span>%d%s</span>" % (num,comment)
'''my_countに送信。後述の@socketio.on()で指定していないときは、socketio.emitとし、namespaceを指定する必要あり。
namespaceとmy_countについてはsocket.html内のjQueryで受け取るためのラベルになってます。
contentが送信するデータです。'''
socketio.emit('my_count', {'data': content}, namespace='/demo')
# フォームからの入力もリアルタイムに受け取る
@app.route('/websocket',methods=['GET','POST'])
def websocket():
'''上で作ったgackground(comment)を実行します。whileで続くのにreturnできちゃうのが不思議すぎる。
この場合、ページに「○秒が経過」と毎秒表示してくれます。'''
socketio.start_background_task(target=background, comment='秒が経過')
return render_template('socket.html',
async_mode = socketio.async_mode,
title = SiteInfo.title,
)
# scketioの設定
@socketio.on('receive_content', namespace='/demo') # scket.html側の/demoからreceive_contentに対して送られてきた場合
def send_content(sent_data): # sent_data:受け取ったデータの名前を好きに設定できます。
content = '<li>%s</li>' % sent_data['data'] # socket.html側で{data:content}としているのでこうなる。
'''テコ入れしたデータをsocket.htmlのmy_contentに送信。
broadcast=Trueは同じnamespaceにアクセスしている人全員の画面に反映させちゃいます。
チャット用とかオンラインゲームの管理人からのメッセージ向けだね。'''
emit('my_content', {'data': content}, broadcast=False)
if __name__ == "__main__":
socketio.run(app, host='127.0.0.1', port=5000, debug=True) # debug=Trueはサーバー起動中の修正もすぐに反映されるので、運用時にはFalseにすること
index.html
layout.htmlは割愛します。わからなかったらコメントください。
{% extends "layout.html" %}
{% block script %}{% endblock %}
{% block body %}
<a href="/websocket">リアルタイム通信へ</a>
<!-- main.pyのrequest.form['single']に届きます -->
<form class="" action="/" method="post">
<input type="text" name="single" value="あいうえお" placeholder="テキスト">
<input type="submit" name="" value="テキストで送信">
</form>
<!-- main.pyのrequest.form.getlist('list')に届きます -->
<form class="" action="/" method="post">
<input type="checkbox" name="list" value="かきくけこ" placeholder="テキスト">かきくけこ
<input type="checkbox" name="list" value="さしすせそ" placeholder="テキスト">さしすせそ
<input type="submit" name="" value="リストで送信">
</form>
<!-- html内for文例。value_listがあれば表示されます -->
{% for v in value_list %}
<li>{{ v }}</li>
{% endfor %}
<!-- html内if文例。value_singleを受け取れば表示されます -->
{% if value_single %}
<li>{{ value_single }}</li>
{% endif %}
{% endblock %}
socket.html
{% extends "layout.html" %}
{% block script %}
<script type="text/javascript" src="//code.jquery.com/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/demo'; //main.pyで指定したnamespace
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
//カウントはこちらで受信。main.py側からmy_count宛に送られたデータを受け取る
socket.on('my_count', function(msg) {
$('.count').empty(); // <div class="count"></div>の中身を消し去ります。
$('.count').append().html(msg.data); // <div class="count"></div>内に、受け取ったdataをhtmlで挿入します。
});
//テキストエリアはこちらで受信。main.py側からmy_content宛に送られたデータを受け取る
socket.on('my_content', function(msg) {
$('#place').append(msg.data); // <div id="place"></div>内に、受け取ったdataを挿入します。
});
//htmlのフォームがsubmitされた時に、main.pyのreceive_content宛にテキストエリアのid="input_data"の値を送信します。
$('form#send_content').submit(function(event) {
socket.emit('receive_content', {data: $('#input_data').val()});
return false; //この意味はわからん
});
});
</script>
{% endblock %}
{% block body %}
<div class="count"></div>
<form id="send_content" method="POST" action='#'>
<input type="text" name="input_data" id="input_data" placeholder="Input content!">
<input type="submit" value="送信">
</form>
<div id="place"></div>
{% endblock %}
以上です!
誰か困っている人に届くことを願う。