/// BANGBOO BLOG ///

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31


September 2024 List
Flask on Sep 05, 2024 11:34 PM

September 5, 2024

Flask
■formaction (hidden以藹??で縺?Submit先変更方觸??) 
buttonや input縺?type=submit/image に臀??荳?できる 
ベースとなるの縺?form縺?id縺?formタ繧?<input... form="form縺?id">

<form id="form" method="post">
<button type="submit" formaction="app.html" name="transition" value="a" form="form">戻る</button>
<button type="submit" formaction="ok.html" name="transition" value="b" form="form">送菫?</button>
下記のような藹??性がある
 formaction
 formenctype
 formmethod
 formnovalidate
 formtarget

■jinja2

///dict
my_dic['name'] = 1
return render_template('index.html', message=my_dic)
テンプレ蛛?
{{message.name}}

///リスト
num_list = np.arange(10)
return render_template('index.html', message=num_list)
テンプレ蛛?
{% for num in num_list %}
<div>{{ num }}</div>
{% endfor %}

///改鐔??
{%- xxx -%} jinjaタグの前後マイナスで改鐔??空白を除藹??する
+縺?trim_blocks(改鐔??詰め)、Istrip_blocks(空白詰め)を無効化する設藹??で俺は使繧?ない
from jinja2 import Environment
jinja_env = Environment()
jinja_env.trim_blocks = True
jinja_env.Istrip_blocks = True

///置觸??
{{ "aaagh" | replace("a","oh,","2") }}
-> oh,oh,agh
{{ input['txtarea'] | replace("\n","<br />") }}

///エスケープ
safeフィルタがなけれ縺?htmlエスケープがかかる
{{ output | safe }}
あるい縺?
{{% autoescape False %}}{{ output }}{{% endautoescape %}}

///コメント(複数鐔??ok)
{# note: commented-out we no longer use this
 {% for user in users %}
 窶?
#}

///生
{% raw %}
<ul>
{% for item in seq %}
<li>{{ item )}</li>
{% endfor %}
</ul>
{% endraw %}

///extend 縺? block 縺? include
笳?flask
page=page_a.model(user)
return render_template("layout.html",title=title,page=page)

笳?layout.html (テンプレ切替と共通ブロック、共通ブロックはテンプレに書いた方分かり易い?)
{% if page['destination']== 'confirm' %}
{% extends "template_confirm.html" %}
{% elif page['destination']== 'complete' %}
{% extends "template_complete.html" %}
{% else %}
{% extends "template_html" %}
{% endif %}
{% block body %}
{% include "header.html" %}
<p>content here.</p>
{% endblock %}

笳?template.html
<html>
<body>
<h1>{{ title }}</h1>
{% block body %}
{% endblock %}

#0以臀??の入力データの確認 (空の確認やintへのキャスト)
#{% if 'inquiry_id' in page['input'] and page['input']['inquiry_id'] is not none and page['input']['inquiry_id'][int > 0 %}

{% if page[input']['inquiry_subject'] %}
<input name="inquiry_subject" type="text" placeholder="例)a" value="{{ page['input']['inquiry_subject'] }}">
{% else %)}
<input name="inquiry subject" type="text" placeholder="例)a">
{% endif %}

{% include "footer.html" %}
</body>
</html>

笳?header.html
<p>date:2024-8-27</p>

笳?footer.html
<p>author.p</p>

笳?page_a.py
def model(user):
value_return = ""
input = {}
error = {}
value_return <br>under constructions taken place by user '<br>'

#入力があった、
if request.method == "POST":
transition = request form.get("transition")
#get, post, 藹??得するもので書き方が違う
#request.args.get("inquiry_id")
#request.form.get("inquiry_id"
#request.json.get("inquiry_id")
logging warming('#####' + user + 'transition: ' + str(transition)+'#####')

if transition == "new" or transition == "error" or transition == "confirm back":
flag_ng=0
#入力値の判藹??
error_txt_inquiry_subject = []
inquiry_subject = request.form.get("inquiry_subject")
ff not checkRequire(inquiry_subject):
error_txt_inquiry_subject.append("件名が空觸??です”)
flag_ng=1

input[inquiry_subject] = inquiry_subject
if flag_ng == 1:
error['error_txt_inquiry_subject] = error_txt_inquiry_subject
#エラー画面を出す
destination = "error"
else:
#確鐔??画面を出す
destination = "confirm"
#End of transition == "new" or transition == "error" or transition == "confirm back":
else:
#transition == "confirm_proceed"
#登録処理し完了画面を出す
destination = "complete"
else:
#初期画面を出す
destination = "new"
return ("value":value_return, "destination":destination, "input":input, "error":error)


■改鐔??
プレースホルダー内での改鐔??縺?&#13:に置き觸??える
<textarea placeholder="例) aaa&#13;bbb">

JINJA2のフォーム入力後の確認HTMLの改鐔??縺??
置觸??で縺?htmlエスケープが觸??かり<br>がそのまま表示されてしまいダ繝?
{{ input | replace("\n", "<br>") }}

htmlエスケープ(HTMLエスケープ縺?<>&のみだった)
窶? 入力があれ縺?htmlエスケープ+改鐔??<br/>変觸??
窶? html表示はそのま縺?htmlエスケープ+改鐔??<br/>変觸??状態で出力
窶? DBにそのま縺?htmlエスケープ+改鐔??<br/>変觸??状態で出力入れる
窶? htmlフォーム内表示縺?htmlエスケープを解除し表示

入力値はそのま縺?input変数に臀??持
confirm画面でエスケープ (html.escape()+改鐔??<br/>変觸??) しescape変数に臀??持
DB保存時に縺?escape変数にプラスしてダブ繝?/シング繝?/バッククォート、セミコロン、バックスラッシュをエスケープし保存
DBから藹??り出す際はそれらをアンエスケープしescape変数に臀??持
Docへ縺?input変数で臀??存
画面表示時はアンエスケープ (改鐔??<br />変觸??+html.unescape())

def escapeHtmlBr(text):
if text is None:
return text
elif isinstance(text, list):
list_escaped = [html.escape(item) for item in text]
list_escaped [item.replace("\n', '<br>') for item in list_escaped]
return list_escaped
else:
escaped_text = html escape(text)
return escaped_text.replace('\n', '<br>')

def unescapeHtmlBr(text):
if text is None:
return text
elif isinstance(text, list):
list_unescaped = [item.replace('<br>', '\n') for item in text]
list_unescaped = [html.unescape(item) for item in list_unescaped]
return list_unescaped
else:
text text.replace('<br>', '\n')
return html.unescape(text)

def escapeDB(text):
if text is None:
return text
elif isinstance(text, list):
list_escaped = [item.replace(';', '&semi;') for item in text]
list_escaped = [item replace('"', '&quot;') for item in list_escaped]
list_escaped = [item.replace("'", '&apos;') for item in list_escaped]
list_escaped = [item.replace('\\', '&bsol;') for item in list_escaped]
list_escaped = [item.replace('`', '&#096;') for item in list_escaped]
return list_escaped
else:
escaped_text = text.replace(';', '&semi;')
escaped_text = escaped_text.replace('"', '&quot;') 
escaped_text = escaped_text.replace("'", '&apos;')
escaped_text = escaped_text.replace('\\', '&bsol;')
escaped_text = escaped_text.replace('`', '&#096;')
return escaped text

def unescapeDB(text)
if text is None
return text
elif isinstance(text, list):
list_unescaped = [item replace('&#096;', '`') for item in text]
list_unescaped [item.replace('&bsol;','\\') for item in list_unescaped]
   list_unescaped [item.replace('&apos;', "'") for item in list_unescaped]
list_unescaped [item.replace('&quot;', '"') for item in list_unescaped]
list_unescaped [item.replace('&semi;', ';') for item in list_unescaped]
return list_unescaped
else:
unescaped_text = text.replace('&#096;','`')
unescaped_text = unescaped_text.replace('&bsol;', '\\')
unescaped_text = unescaped_text.replace('&apos;', "'")
unescaped_text unescaped_text replace('&quot;', '"')
unescaped_text = unescaped_text.replace('&semi;', ';')
return unescaped_text

■文字確鐔??
def check_special_characters(a):|
#チェックする記号のセット
special_characters = ['<', '>', '"', '&']
#特藹??の鐔??号が含まれているかをチェッ繧?
if any(char in a for char in special_characters):
raise ValueError(f"変謨? 'a' に軆??止されている文字が含まれています: {a}")
try:
a = "Hello & World"
check_special_characters(a)
except ValueError as e:
print(e)

■DB縺?null行の觸??髯?
bq = bigquery.Client()
sql = f"""SELECT a FROM `ds.b' WHERE c = '{pri}'"""
results=bq.query(sql)
list = list()
for row in results:
if row.a is not None:
list.append(str(row.a))

■Flask-WTF CCSRF対軆??縺?hiddenに入れる
https://qiita.com/RGS/items/c8c99970054a481ac80d
requrement.txt Flask-WTF==1.2.1

from flask_wtf import CSRFProtect
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecretkey'
csrf = CSRFProtect(app)

<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>

PRGパターン縺?GETで藹??了画面に鐔??縺?がエラーとならない、WTF縺?POSTのみ利縺?ようだ

●完了画面でセッション藹??数がない場合はエラーとする
保存処理縺?doc_idをセッション藹??数に入れ
不正で直接完了画面にい縺?とエラーとする
https://xxxx.com/complete?doc id=ddd

■重複登録の回避軆??(PRG方藹??+JSボタン無蜉?)
・リダイレクト POST/Redirect/GET パター繝?
・連打を避けるためボタン縺?JS無効化
片方のみのためquerySelectorAll() を使う>ページ遷移しな縺?なった>下記JSを使う

@app.route('/submit', methods=['POST'])
def page():
page = model
if complete:
session['doc_id'] = page['input']['doc_id']
redirect(url_for('thank_you'))
else:
retrun template('layout.html')

@app.route('/thank_you')
def thank_you():
if 'doc_id' in session:
page['input']['doc_id"] = session['doc_id"]
session clear()
return render_template('complete_layout.html', page=page)
else:
return render_template('error.html", message="missing arg")

@app.errorhandler(404)
def page_not_found(e)
return render_template('error html', message="Page not found"), 404

error.html蛛?
<h1>{{message }}</h1>

submit蛛?
連打を避けるためボタンの無効化
片方のみのためquerySelectorAll() を使うとページ遷移しな縺?なる等がある
<script>
document.addEventListener('DOMContentLoaded', function(){
const form = document.getElementById('form');
const buttons = form.getElementsByTagName('button');
form.onsubmit= function(event) {
//クリックされたボタンを藹??得
const clickedButton = event.submitter;
//隠しフィールドを追加して、ボタン縺?name 縺? value を送菫?
const hiddenField = document.createElement('input');
hiddenField type = 'hidden'
hiddenField.name = clickedButton.name;
hiddenField.value = clickedButton value;
form.appendChild(hiddenField);
// すべてのボタンを無効化して臀??重送信を防止
for (let i = 0; i < buttons.length; i++) {
buttons[i].disabled = true;
}
}
}
</script>

Posted by funa : 11:34 PM | Web | Comment (0) | Trackback (0)