/// 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

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>

twitter
Hatena
Google Buzz
newsing
Yahoo!
Buzzurl
Technorati
del.icio.us
Choix
Iza!
Livedoor Clip
Facebook
Evernote
 

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


PhotoGallery


TWITTER
Search

Mobile
QR for cellphone  QR for smart phone
For mobile click here
For smart phone click here
Popular Page
#1Web
#2Hiace 200
#3Gadget
#4The beginning of CSSレイアウト
#5Column
#6Web font test
#7Ora Ora Ora Ora Ora
#8Wifi cam
#9みたらし団子
#10Arcade Controller
#11G Suite
#12PC SPEC 2012.8
#13Javascript
#14REMIX DTM DAW - Acid
#15RSS Radio
#16Optimost
#17通話SIM
#18Attachment
#19Summer time blues
#20Enigma
#21Git
#22Warning!! Page Expired.
#23Speaker
#24Darwinian Theory Of Evolution
#25AV首相
#26htaccess mod_rewite
#27/// BANGBOO BLOG /// From 2016-01-01 To 2016-01-31
#28竹書房
#29F☆ck CSS
#30Automobile Inspection
#31No ID
#32Win7 / Win10 Insco
#33Speaker
#34Arcade Controller
#35Agile
#36G Suite
#37Personal Information Privacy Act
#38Europe
#39Warning!! Page Expired.
#40GoogleMap Moblile
#41CSS Selectors
#42MySQL DB Database
#43Ant
#44☆od damnit
#45Teeth Teeth
#46Itinerary with a eurail pass
#47PHP Developer
#48Affiliate
#49/// BANGBOO BLOG /// From 2019-01-01 To 2019-01-31
#50/// BANGBOO BLOG /// From 2019-09-01 To 2019-09-30
#51/// BANGBOO BLOG /// On 2020-03-01
#52/// BANGBOO BLOG /// On 2020-04-01
#53Windows env tips
#54恐慌からの脱出方法
#55MARUTAI
#56A Rainbow Between Clouds‏
#57ER
#58PDF in cellphone with microSD
#59DJ
#60ICOCA
#61Departures
#62Update your home page
#63CSS Grid
#64恐慌からの脱出方法
#65ハチロクカフェ
#66/// BANGBOO BLOG /// On 2016-03-31
#67/// BANGBOO BLOG /// From 2017-02-01 To 2017-02-28
#68/// BANGBOO BLOG /// From 2019-07-01 To 2019-07-31
#69/// BANGBOO BLOG /// From 2019-10-01 To 2019-10-31
#70/// BANGBOO BLOG /// On 2020-01-21
#71Bike
#72Where Hiphop lives!!
#73The team that always wins
#74Tora Tora Tora
#75Blog Ping
#76無料ストレージ
#77jQuery - write less, do more.
#78Adobe Premire6.0 (Guru R.I.P.)
#79PC SPEC 2007.7
#80Google Sitemap
#81Information privacy & antispam law
#82Wifi security camera with solar panel & small battery
#83Hope get back to normal
#84Vice versa
#85ハイエースのメンテ
#86Camoufla
#87α7Ⅱ
#88Jack up Hiace
#89Fucking tire
#90Big D
#914 Pole Plug
#925-year-old shit
#93Emancipation Proclamation
#94Windows env tips
#95Meritocracy
#96Focus zone
#97Raspberry Pi
#98Mind Control
#99Interview
#100Branding Excellent
Category
Recent Entry
Trackback
Comment
Archive
<     February 2025     >
Sun Mon Tue Wed Thi Fri Sat
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
Link