Python Web

HTML Web API Flask資料 Flask-SQLAlchemy 画像投稿サイト作成 MkDocs

Flaskの基本

Webフレームワーク。pip install flaskでインストール。

ルーティングと実行

Flaskオブジェクトを作り(例:app)、@app.getデコレータでURLを指定する。その下の関数で文字列を返す。

実行はapp.run()。

from flask import Flask

app = Flask(__name__)

@app.get('/hello')
def hello():
    return "Hello World!"

app.run()

これで http://localhost:5000/hello にアクセスすると Hello World! が表示される。

app.run()のオプション

第一引数はホストIPアドレス指定。何も指定しないと127.0.0.1(=localhost)。しかし、この状態では外部から接続できない。外部接続可能にするにはIPアドレス0.0.0.0を指定する。

app.run("0.0.0.0") 

ポート番号はデフォールトで5000。これを返るにはportを指定する。

app.run("0.0.0.0",port="8080") # ポート番号指定

デバッグモードはデフォールトでFalseなので、有効にするにはdebug=Trueを指定する。これを指定すると、エラー内容がブラウザに表示される。

app.run("0.0.0.0",debug=True)

テンプレート

templateフォルダを作成し、HTMLファイルを用意し、それを表示する。

テンプレート内に {{名前}}で変数を入れる場所を指定する。

<html>
<body>
<h1>Top</h1>

<p>{{mes}}</p>
</body>
</html>

python関数内では、render_template関数でテンプレートファイル名を指定する。
渡すデータを「名前=データ」で指定する。

from flask import Flask, render_template
app = Flask(__name__)

@app.get('/hello')
def hello():
    return render_template("sample.html",mes="こんにちは")

app.run(debug=True)

リストの表示

リストを渡し、テンプレート側で全て表示する例

@app.get('/hello')
def hello():
    names = ["山田","田中","佐藤"]
    return render_template("sample.html",names=names)

テンプレート側では行頭に {% %} 内に通常のpythonスクリプトのように書ける。
ただし、forの最後には endfor 、ifの最後には、endifを書く。

{% for name in names %}
   <p>{{ name }}</p>
{% endfor %}

現在のループ回数の取得は、loop.index で可能。これは1から数えるので、0からの場合には、loop.index0 とする。

tableのサンプル

<table>
{% for s in slist %}
    <tr>
    <td>{{s.sid}}</td>
    <td>{{s.sname}}</td>
    <td>{{s.tanka}}</td>
    </tr>
{% endfor %}
</table>

ifのサンプル

{% if tanka>200 %}
   高額
{% else %}
   低額
{% endif %}

変数のセット

{% set num = 10 %}
{{num}}

テンプレートの継承

全ファイル共通のテンプレートを作り、各ファイルで使うことが出来る。

<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>

{% block content %}
<!-- ここに個別ファイルが入る -->
{% endblock %}

</body>
</html>

このファイルがtemplate.htmlの場合、これを使う個別ファイルは以下のように書く

{% extends "template.html" %}
{% block content %}
<h1>Top</h1>

<p>{{mes}}</p>
{% endblock %}

HTML埋め込み

HTMLをそのまま出したい場合「 | safe」を付ける。ただし、クロスサイトスクリプティングの危険性に配慮すること。確実に安全なデータにしか使うことは出来ない。

{{ name | safe}}

静的ファイル

css、JavaScript、画像などの静的ファイルは /static フォルダに置き、/static/ファイル名 でアクセスする。

URLパスからのデータ取得

/hello/abc などのabc部分を受け取る

@app.get('/hello/<name>')
def hello(name):
    return render_template("sample.html",mes=name + "さんこんにちは")

app.run(debug=True)

/hello/10 など数値だけを受け取る

@app.get('/hello/<int:num>')
def hello(num):
    return render_template("sample.html",mes="数値:" + str(num))

app.run(debug=True)

フォームからのデータ取得

<p>{{num}}</p>

<form method="post">
<input type="text" name="num">
<input type="submit" value="送信">
</form>

POSTのデータ取得は request.form['名前']

@app.post('/hello')
def do_hello():
    num = request.form['num']
    return render_template("form.html",num=num)

GETのデータ取得は request.args['名前']

@app.get('/hello')
def hello():
    num = request.args['num']
    return template("form.html",num=num)

GETのデータ取得で未指定時のデフォールトを指定するには、request.args.get('名前', 'デフォールト値')

@app.get('/hello')
def hello():
    # numの指定が無いときには100
    num = request.args.get('num', 100)
    return template("form.html",num=num)

同じ名前を指定した部品から複数の値をリストで受け取るには、getlistを使う。

@app.get('/hello')
def hello():
    nums = request.form.getlist('nums')
    return template("form.html",nums=nums)

リダイレクト

リダイレクトは redirect をインポートして以下を行う。

# / アクセス時 helloにリダイレクト
@app.get('/')
def index():
    return redirect('/hello')

セッション

セッションは まずsession をインポート

from flask import Flask, redirect, render_template, request, session 

まず app.secret_key を設定する。

app.secret_key = b'\x83\xc9L:\xdf\x8a\x97\xb9\xef\xc4}G\t\xed\x1b('

キーはコマンドラインから以下のようにして生成する。

python -c "import os; print(os.urandom(16))"

セッションはsessionという名前の辞書として値を代入する。

session['name'] = name

ログインしているかの確認例

@app.get('/')
def index():
    if 'name' in session:
        name = session['name']
        return render_template("hello.html",title="ログイン",name=name)
    else:
        return redirect('/login')

ログアウト

    session.pop('name', None)

フラッシュメッセージ

セッション使用時に、フラッシュメッセージを利用可能。一度のみ使えるメッセージでエラーメッセージなどに使用する。

Python内でメッセージを指定

flash("IDまたはパスワードが違います")

HTML内でメッセージを表示

{% for message in get_flashed_messages() %}
    <p>{{ message }}</p>
{% endfor %}

カテゴリ指定

flash("IDまたはパスワードが違います","error")

HTML内でカテゴリをタグのclassに指定。classごとに色分けする。

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

ファイルのアップロード

画像ファイルサンプル

HTMLフォーム

/fileupに作成

<form action="/up" method="post" enctype="multipart/form-data">
<input type="file" name="upfile">
<input type="submit" value="アップロード">
</form>

Python

@app.post('/up')
def up():
    if 'upfile' not in request.files:
        flash("ファイルがありません", "error-info")
        return redirect(f'/fileup')
    
    file = request.files['upfile']
    orgname = file.filename
    
    if orgname=='':
        flash("ファイルが選択されていません", "error-info")
        return redirect(f'/fileup')

    # c:\pythonに保存する場合
    fname = 'c:\\python\\{orgname}'
    file.save(fname)

    return redirect(f'/fileup')

複数ファイルでの開発

通常のFlaskは1ファイルに全てのURLに対応する関数を定義する。複数ファイルに分ける場合、パッケージに分けて対処する。

まず、フォルダの配下にwebなどのフォルダを作り、そこにFlask用のファイルを置く。データベース用モジュールは別途dbフォルダに置く

フォルダ構成の例

フォルダファイル名内容
直下app.py実行ファイル
web__init__.pyFlaskのappを生成するファイル。ファイル名は固定
webmodule1.py分割したファイルその1。
webmodule2.py分割したファイルその2。
web/templatesXXX.htmlなどテンプレートファイル。
web/staticXXX.cssなど静的ファイル。
dbshouhin.pyデータベース用のモジュール。

app.py

このファイルを実行する。

from web  import app

app.run(debug=True)

__init__.py

webフォルダ配下に必ずこのファイル名で置く。これにより、webパッケージの初期化を行う。

from flask import Flask
app = Flask(__name__)

# モジュールを全てインポート
import web.module1
import web.module2

module1.py

appをインポートし、あとは通常と同じ

from flask import Flask, render_template, request, redirect
from web import app

@app.get('/')
def module1():
    return render_template('module1.html')

module2.py

データベースのモジュールが/dbに置かれているときの使用例。db.shouhinでインポート。

from flask import Flask, render_template, request, redirect
from web import app
import db.shouhin

@app.get('/shouhin')
def shouhin_find():
    row = db.shouhin.find(1)
    return render_template('shouhin.html', shouhin = row)