Flask (Python3)でのformおよびajax通信等から送信されたPOSTデータやファイルの受け取り

Flaskを使い、formやajax通信のPOSTデータを受け取る方法をご紹介します。(というより単なる個人的備忘録です)

Flaskのサーバー機能やルーティングンの詳細には触れませんが、以下のサンプルでは /api/rcv_postdata/<id>/ のようにidパラメータが入るケースを想定して記述しています。

POSTデータは、formのように綺麗に整形されて入ってくるものとjson形式でbodyにセットされてくるパターンがあります。
Flaskでは、request.formやrequest.dataでどちらも簡単に取得することができます。

ここではついでにmultipartでの添付ファイルを受け取る場合の処理も記述しておきます。

下記処理ではformからのデータを確認して、なければbody側のデータを確認するように書いています。
一通りのデータは文字列で入ってくるため、その先に何か処理を書く際に使い勝手が良いように型変換を行っています。

isdecimal()で数値判定をしてしまうと、Pythonの場合は全角だったり漢数字もTrueを返してしまいます。
さらにマイナスが付くとFalseになったりするので、微妙に使い勝手がよくありません。

半角文字であることを判定するためのisascii()と組み合わせて使おうかと思ったのですが、Python3.7からで環境によって使えなかったりするので、正規表現でintとfloatの判定をしています。
小数点はただ見つけているだけですが、本当は1個だけという処理にした方が良かったかもしれません。
ちなみに、isascii()はアスキー文字の判定のため、マイナス記号や小数点が入っていてもTrueが返ります。

from datetime import datetime
from flask import Flask, request
from werkzeug.utils import secure_filename
import ast
import datetime as dt
import os
import re
import time

upload_dir = '/mnt/ssd/uploads'

app = Flask(__name__)

@app.route("/api/rcv_postdata/<id>/", methods=['POST'])
def rcv_postdata(id):

    postdata = {}

    # formからのPOST, multipartデータはImmutableMultiDictで来る
    print(request.form)
    print(request.files)

    # ajax等でのjsonで送信されたPOSTデータはbody部に入るのでByteString
    print(request.data)

    if len(request.form) > 0:
        postdata = request.form.to_dict(flat=True)
    elif len(request.data) > 0:
        try:
            postbody = request.data.decode('utf-8')
            postdata = ast.literal_eval(postbody)
        except:
            pass

    # 添付ファイル
    filename = ''
    if len(request.files) > 0:
        for key in request.files:
            print(request.files[key])
            file = request.files[key]
            if file.filename != '':
                filename = secure_filename(file.filename)
                file.save(os.path.join(upload_dir, filename))

    # filenameやupload_dirはpostdataに入れておくと便利かも
    if filename != '':
        postdata['filepath'] = os.path.join(upload_dir, filename)

    # URLに付与されたidも後段で使いやすいようpostdata配列に入れておくが、idという項目がPOSTされる場合はここで上書きしてしまうので、キー名は使い勝手が良いように書き換える
    postdata['id'] = id

    # formからのデータは文字列になっているため、正しい型に変換する
    for key in postdata:
        if key == 'timestamp':
            # datetime項目は項目名とフォーマットを調べて記述する
            try:
                postdata[key] = datetime.strptime(postdata[key], '%Y-%m-%d %H:%M:%S')

                # 好きな形式の文字列に変換したい場合はさらに変換する
                postdata[key] = postdata[key].strftime('%Y-%m-%dT%H:%M:%S.%f')
                # or isoformat
                # postdata[key] = postdata[key].isoformat()
            except:
                pass
        elif isinstance(postdata[key], str):
            if re.search('^(\-?[0-9\.]+)$', postdata[key]):
                if postdata[key].find('.') != -1:
                    try:
                        postdata[key] = float(postdata[key])
                    except:
                        pass
                else:
                    try:
                        postdata[key] = int(postdata[key])
                    except:
                        pass
            elif postdata[key].find('[') == 0 and postdata[key][-1] == ']':
                # 文字列としてlistが来た場合。dictも対応する場合は同様に{}を設定する
                try:
                    postdata[key] = json.loads(postdata[key])
                except:
                    pass
            else:
                pass
        elif isinstance(postdata[key], (int, float)):
            # 元からint型やfloat型の項目
            pass
        else:
            # 該当しないものはとりあえずstr関数で文字列化しておく
            postdata[key] = str(postdata[key])

        # なんとなく型変換できているので、この先の各種処理で触りやすい形となっている
        print(postdata)

    return

if __name__ == "__main__":
    app.run(host='0.0.0.0',port=5000,debug=True)

Flask (Python3)でのformおよびajax通信等から送信されたPOSTデータやファイルの受け取りにコメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です