SQLの窓

2020年09月01日


XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 1 )

最初は、基本的なデータ型の表示テストです。



XAMPP で Python を実行できるようにするには、httpd.conf の 『AddHandler cgi-script .cgi .pl .asp』 に .py を追加します


( Apache 環境では、.py に対してソースコードの先頭の #!プログラム をcgi として実行します / PHP は別定義です )

Windows では、Python の先頭に Python の実行プログラムの場所を記述しなくても、Windows のレジストリに設定して動作できるようにする事ができるようです
( 💘 ScriptInterpreterSource Registry-Strict )

以下は、Windows 環境下で .py に対して実行される処理として設定される内容です
Python のダウンロードとインストールは こちら(Windows 環境のPython) がとても参考になります 上記リンク先のフル・インストーラ版で python-3.8.5-amd64.exe をインストールすると、拡張子の関連付けで .py に対して 『"C:\WINDOWS\py.exe" "%L" %*』が登録されます Python.File は、ファイルのタイプとして登録される詳細ですが、このエントリが拡張子と関連付けられます。確認は、コマンドプロンプトで 『assoc .py』で確認できますが、レジストリでも確認できます 以下のプロパティはインストールされた py.exe と python.exe です 以下は、仮想ディレクトリの作成です
<IfModule alias_module>

    Alias /py "/app/py20"
    <Directory "/app/py20">
        Options Indexes FollowSymLinks Includes ExecCGI
        AllowOverride All
        Require all granted
    </Directory>

</IfModule>
sample_01.py
#!C:\python\python.exe

import cgi
import cgitb
cgitb.enable()

import sys
import io
import os
import urllib.parse
from xml.sax.saxutils import *

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

print("Content-Type: text/html; charset=utf-8")
print( "Expires: Thu, 19 Nov 1981 08:52:00 GMT" )
print( "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0" )
print( "Pragma: no-cache" )
print()

data1 = "こんにちは"
data2 = [1,2,3,4]
data3 = (1,2,3,4,"")
data4 = {"A":1, "B":2, "C":3, "D":4 }

type1 = str(type(data1))
type2 = str(type(data2))
type3 = str(type(data3))
type4 = str(type(data4))

print( data1 + "<br>" )
print( escape(type1) + "<br>" )

print( str(data2) + "<br>" )
print( escape(type2) + "<br>" )

print( str(data3) + "<br>" )
print( escape(type3) + "<br>" )

print( str(data4) + "<br>" )
print( escape(type4) + "<br>" )



関連する記事

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 1 )

XAMPP + Python( 3.8 ) でWEBアプリの基礎部分構築 : その ( 2 ) : QUERY_STRING と 画面定義

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 3 ) : cgi.FieldStorage() から ディクショナリ

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 4 ) : リダイレクトと関数とログ出力



posted by lightbox at 2020-09-01 11:15 | Python | このブログの読者になる | 更新情報をチェックする

2020年08月20日


XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 4 ) : リダイレクトと関数とログ出力

def を使用して ファイルにログを書き込む処理を定義し、REQUEST_METHOD で判断して POST 時にリダイレクトを行います



XAMPP で Python を実行できるようにするには、httpd.conf の 『AddHandler cgi-script .cgi .pl .asp』 に .py を追加します



Windows では、Python の先頭に Python の実行プログラムの場所を記述しなくても、Windows のレジストリに設定して動作できるようにする事ができるようです
( 💘 ScriptInterpreterSource Registry-Strict )

Python のダウンロードとインストールは こちら(Windows 環境のPython) がとても参考になります

sample_04.py

REQUEST_METHOD を取得して、POST 時に HTTP ヘッダにリダイレクト処理を入れたり、ログ出力を実行するようにしています
if method == "POST":
	print( "Location: sample_04.py?field3=%E5%B1%B1%E7%94%B0%20%E5%A4%AA%E9%83%8E" )

#!C:\python\python.exe

import cgi
import cgitb
cgitb.enable()

import sys
import io
import os
import urllib.parse
from xml.sax.saxutils import *

# **************************************
# ログ出力
# **************************************
def log( message ):
	with open('debug.log', 'a') as f:
		print(message,end='\n',file=f)

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

method = os.environ["REQUEST_METHOD"]

print("Content-Type: text/html; charset=utf-8")
print( "Expires: Thu, 19 Nov 1981 08:52:00 GMT" )
print( "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0" )
print( "Pragma: no-cache" )
if method == "POST":
	print( "Location: sample_04.py?field3=%E5%B1%B1%E7%94%B0%20%E5%A4%AA%E9%83%8E" )
print()

form = cgi.FieldStorage()

# form 用データの内容です
result = str(form) + "<br>"
if method == "POST":
	log( form )

form_data = {}
fields = [ "field1", "field2", "field3", "field4", "send" ]
for field_name in fields:
	if field_name not in form:
		form_data[field_name] = ""
	else:
		form_data[field_name] = form.getvalue(field_name)

# 以降で使用可能なディクショナリの内容です
result += str(form_data) + "<br>"
if method == "POST":
	log( form_data )

view = f"""<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css">

<style>
#main {{
	padding: 30px;
	font-size: 24px;
}}

form {{
	margin-bottom: 20px;
}}

.inline {{
	display: inline-block;
}}
.ttl {{
	width: 100px;
}}
</style>
</head>
<body>
<div id="main">

	<form method="post">
		<div>
			<div class="inline ttl">氏名</div>
			<div class="inline"><input type="text" name="field3" value="{form_data["field3"]}"></div>
		</div>

		<div>
			<div class="inline ttl">フリガナ</div>
			<div class="inline"><input type="text" name="field4" value="{form_data["field4"]}"></div>
			<div class="inline ml-2"><input type="submit" name="send" value="送信"></div>
		</div>
	</form>

	{result}

</div>
</body>
</html>"""

print(view)


関連する記事

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 1 )

XAMPP + Python( 3.8 ) でWEBアプリの基礎部分構築 : その ( 2 ) : QUERY_STRING と 画面定義

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 3 ) : cgi.FieldStorage() から ディクショナリ

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 4 ) : リダイレクトと関数とログ出力


posted by lightbox at 2020-08-20 16:54 | Python | このブログの読者になる | 更新情報をチェックする

2020年08月19日


XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 3 ) : cgi.FieldStorage() から ディクショナリ

Python の CGI の機能を使用して form の内容( GET と POST 混在 )を取得して使用します



XAMPP で Python を実行できるようにするには、httpd.conf の 『AddHandler cgi-script .cgi .pl .asp』 に .py を追加します



Windows では、Python の先頭に Python の実行プログラムの場所を記述しなくても、Windows のレジストリに設定して動作できるようにする事ができるようです
( 💘 ScriptInterpreterSource Registry-Strict )

Python のダウンロードとインストールは こちら(Windows 環境のPython) がとても参考になります

sample_03.py
form_data = {}
fields = [ "field1", "field2", "field3", "field4", "send" ]
for field_name in fields:
	if field_name not in form:
		form_data[field_name] = ""
	else:
		form_data[field_name] = form.getvalue(field_name)
cgi.FieldStorage() で取得された from をディクショナリである form_data に変換して使用します
#!C:\python\python.exe

import cgi
import cgitb
cgitb.enable()

import sys
import io
import os
import urllib.parse
from xml.sax.saxutils import *

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

print("Content-Type: text/html; charset=utf-8")
print( "Expires: Thu, 19 Nov 1981 08:52:00 GMT" )
print( "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0" )
print( "Pragma: no-cache" )
print()

print( "<div style='padding:30px;font-size:20px;word-break:break-all;'>" )

form = cgi.FieldStorage()

# form 用データの内容です
print( str(form) + "<br>")

form_data = {}
fields = [ "field1", "field2", "field3", "field4", "send" ]
for field_name in fields:
	if field_name not in form:
		form_data[field_name] = ""
	else:
		form_data[field_name] = form.getvalue(field_name)

# 以降で使用可能なディクショナリの内容です
print(str(form_data) + "<br>")

print( "</div>" )

view = f"""<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css">

<style>
#main {{
	padding: 30px;
	font-size: 24px;
}}

form {{
	margin-bottom: 20px;
}}

.inline {{
	display: inline-block;
}}
.ttl {{
	width: 100px;
}}
</style>
</head>
<body>
<div id="main">
	<form method="get">
		<div>
			<div class="inline ttl">氏名</div>
			<div class="inline"><input type="text" name="field1" value="{form_data["field1"]}"></div>
		</div>

		<div>
			<div class="inline ttl">フリガナ</div>
			<div class="inline"><input type="text" name="field2" value="{form_data["field2"]}"></div>
			<div class="inline ml-2"><input type="submit" name="send" value="送信"></div>
		</div>
	</form>

	<form method="post">
		<div>
			<div class="inline ttl">氏名</div>
			<div class="inline"><input type="text" name="field3" value="{form_data["field3"]}"></div>
		</div>

		<div>
			<div class="inline ttl">フリガナ</div>
			<div class="inline"><input type="text" name="field4" value="{form_data["field4"]}"></div>
			<div class="inline ml-2"><input type="submit" name="send" value="送信"></div>
		</div>
	</form>

</div>
</body>
</html>"""

print(view)


関連する記事

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 1 )

XAMPP + Python( 3.8 ) でWEBアプリの基礎部分構築 : その ( 2 ) : QUERY_STRING と 画面定義

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 3 ) : cgi.FieldStorage() から ディクショナリ

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 4 ) : リダイレクトと関数とログ出力



posted by lightbox at 2020-08-19 16:26 | Python | このブログの読者になる | 更新情報をチェックする

XAMPP + Python( 3.8 ) でWEBアプリの基礎部分構築 : その ( 2 ) : QUERY_STRING と 画面定義

Python の CGI の機能を使わずに、QUERY_STRING を使用して GET メソッドによる入力値の処理を行います。



XAMPP で Python を実行できるようにするには、httpd.conf の 『AddHandler cgi-script .cgi .pl .asp』 に .py を追加します



Windows では、Python の先頭に Python の実行プログラムの場所を記述しなくても、Windows のレジストリに設定して動作できるようにする事ができるようです
( 💘 ScriptInterpreterSource Registry-Strict )

Python のダウンロードとインストールは こちら(Windows 環境のPython) がとても参考になります

sample_02.py

QUERY_STRING から入力値を取得する為に urllib.parse.parse_qs を使用しますが、取得されたディクショナリの key 部分にフィールド名が使用され、値の部分に配列が使用されるので少し使いづらい為、本来は cgi.FieldStorage() を使用して form の値を取得して利用します( GET と POST 混在 )

fields = [ "field1", "field2", "field3" ]
for field_name in fields:
	if form_get.get(field_name) is None:
		form_get[field_name] = [""]
上記処理では、fields に使用する field名 を定義して、入力されなかったフィールドに対して空文字列をセットしています 画面定義に f-string を使用しています。この際注意するのは、CSS や JavaScript で使用される {} を {{}} に変更する事です
#!C:\python\python.exe

import cgi
import cgitb
cgitb.enable()

import sys
import io
import os
import urllib.parse
from xml.sax.saxutils import *

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

print("Content-Type: text/html; charset=utf-8")
print( "Expires: Thu, 19 Nov 1981 08:52:00 GMT" )
print( "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0" )
print( "Pragma: no-cache" )
print()

print( "<div style='padding:30px;font-size:20px;word-break:break-all;'>" )

# QueryString の文字列を取得します
try:
	qs = os.environ["QUERY_STRING"]
except Exception as e:
	qs = ""

# QueryString の文字列です
print(qs + "<br>")

# 値が無い場合は、エントリは作成されません
form_get =  urllib.parse.parse_qs(qs)

# qs が空文字列の場合は form_get は {} となります
print(str(form_get) + "<br>")

# このリストのデータを キーに持つディクショナリ(form_get)を確実に作成します
# form_get は、値が全て配列で、通常 [0] の値を使用します
fields = [ "field1", "field2", "field3" ]
for field_name in fields:
	if form_get.get(field_name) is None:
		form_get[field_name] = [""]

# 以降で使用可能なディクショナリの内容です
print(str(form_get) + "<br>")

print( "</div>" )

view = f"""<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css">

<style>
#main {{
	padding: 30px;
	font-size: 24px;
}}

form {{
	margin-bottom: 20px;
}}

.inline {{
	display: inline-block;
}}
.ttl {{
	width: 100px;
}}
</style>
</head>
<body>
<div id="main">
	<form method="get">
		<div>
			<div class="inline ttl">氏名</div>
			<div class="inline"><input type="text" name="field1" value="{form_get["field1"][0]}"></div>
		</div>

		<div>
			<div class="inline ttl">フリガナ</div>
			<div class="inline"><input type="text" name="field2" value="{form_get["field2"][0]}"></div>
			<div class="inline ml-2"><input type="submit" name="send" value="送信"></div>
		</div>
	</form>
</div>
</body>
</html>"""

print(view)


f-string を使用せずに別ファイルにして import する

こうすると、f-string の使えないバージョンの python でも利用できます。また外部ファイルにする事によって、埋め込まれた内容がわかりにくくなる事を避ける事ができます

import sample_02_view

print( sample_02_view.html.format( form_get["field1"][0], form_get["field2"][0]) )
sample_02_view.py
html = """<!DOCTYPE html>
<html>
<head>
<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css">

<style>
#main {{
	padding: 30px;
	font-size: 24px;
}}

form {{
	margin-bottom: 20px;
}}

.inline {{
	display: inline-block;
}}
.ttl {{
	width: 100px;
}}
</style>
</head>
<body>
<div id="main">
	<form method="get">
		<div>
			<div class="inline ttl">氏名</div>
			<div class="inline"><input type="text" name="field1" value="{}"></div>
		</div>

		<div>
			<div class="inline ttl">フリガナ</div>
			<div class="inline"><input type="text" name="field2" value="{}"></div>
			<div class="inline ml-2"><input type="submit" name="send" value="送信"></div>
		</div>
	</form>
</div>
</body>
</html>"""


関連する記事

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 1 )

XAMPP + Python( 3.8 ) でWEBアプリの基礎部分構築 : その ( 2 ) : QUERY_STRING と 画面定義

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 3 ) : cgi.FieldStorage() から ディクショナリ

XAMPP + Python( 3.8 ) で WEBアプリの基礎部分構築 : その ( 4 ) : リダイレクトと関数とログ出力



posted by lightbox at 2020-08-19 10:41 | Python | このブログの読者になる | 更新情報をチェックする

2018年07月23日


Python + MySQL + IFRAME + Bootstrap : 問い合せ WEB アプリテンプレート





JavaScript は jQuery を使用します。

テンプレート構造

IFRAME 内の処理( req フォルダ内 )が実際の問い合わせ処理になります。メインページの下部に CSS の calc 関数を使用してフィットさせています。



メインの control.py( エントリポイント )

Python の import は PHP の require( または include ) や Ruby の require と違って モジュールを利用する為のメモリ空間の利用方法が厳格です。PHP の require は、単純にソースコードの外部読みこみで、Ruby の require は、仕様としてライブラリを読み込むようになっているのですが、現実としてソースコードレベルの読み込みと考えて問題は無いようです( グローバル変数がソース間で使用可能 )

Python ではソース間のグローバル変数は存在せず、モジュール内の変数を読みこむ側がいかに参照するかというアプローチになります。これに関してもエントリポイントの開始ソースとその他のソースの場合ではモジュール変数の扱いが微妙に違うので注意が必要です( from モジュール import * の動作 )

from settings import * の * で全ての内容をインポートする方法は、Python では推奨されていませんが、ここでは必ずすべて必要になるテンプレートとしての仕様に基づいて使用しています。
import cgitb
cgitb.enable()

# **************************************
# 共有メソッド
# **************************************
from settings import *
set( "base_name", __file__ )

# **************************************
# CGI 初期処理
# **************************************
cgi_init()

import view

settings.py ( 共通処理 )

Python ではソース単位で必要なモジュールは常に import する必要があります。ただ、最終的に実行単位のメモリ空間では同じモジュールは各ソースで共有されるようです。

単純なグローバル変数が使用できないので、settings モジュールに val デイクショナリを作成しておいて、get/set メソッドで値の操作を可能にしてグローバル変数代わりに使用しています。

settings で定義された変数は、settings 内で定義されている メソッド内で global キーワードを使用して参照可能になります( PHP と類似 )

log と pplog はデバッグ用のメソッドで、テキストフアイルに出力する為に使用します。
import cgi
import sys
import io
import pprint

# **************************************
# デバッグログの初期化
# **************************************
with open('debug.log', 'w') as f:
	print("開始",end='\n',file=f)

# **************************************
# グローバル変数
# **************************************
pp = None

# **************************************
# 共有ディクショナリ
# **************************************
val = {}
val["pass"] = "1"
val["check_message"] = ""
val["lines"] = ""

# **************************************
# フォーム用変数
# **************************************
fld_names = {}
form = {}
form_data = {}

# **************************************
# CGI 初期処理
# **************************************
def cgi_init():

	global pp
	global form

	sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
	 
	print("Content-Type: text/html; charset=utf-8")
	print( "Expires: Thu, 19 Nov 1981 08:52:00 GMT" )
	print( "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0" )
	print( "Pragma: no-cache" )
	print()

	pp = pprint.PrettyPrinter(indent=4)

	form = cgi.FieldStorage()

# **************************************
# データの整形表示
# **************************************
def print_r(data):

	global pp

	print("<pre>")
	pp.pprint(data)
	print("</pre>")

# **************************************
# フォームデータの取得
# **************************************
def forms(target, set_data=None):

	global fld_names
	global form

	if set_data is not None:
		form_data[fld_names[target]] = set_data
		return

	result = ""

	if form_data.get(fld_names[target]) is not None:
		return( form_data[fld_names[target]] )

	if not fld_names[target] not in form:
		result = form[fld_names[target]].value

	return(result)

# **************************************
# フォームフィールド名取得
# **************************************
def fields(target):

	return(fld_names[target])

# **************************************
# フォームフィールドセット設定
# **************************************
def set_field_names( field_set ):

	global fld_names

	fld_names = field_set


# **************************************
# ディクショナリ : set & get
# **************************************
def set( my_key, my_val ):

	global val
	val[my_key] = my_val

def get( my_key ):

	global val
	return( val[my_key] )

# **************************************
# ログ出力
# **************************************
def log( message ):
	with open('debug.log', 'a') as f:
		print(message,end='\n',file=f)
	
# **************************************
# ログ出力( pprint )
# **************************************
def pplog( obj ):

	with open("debug.log", "a") as f:
		pprint.pprint(obj, stream=f)




view.py ( メイン画面定義 )

画面定義は、三重クオート文字列と f-string を使用して PHP のヒアドキュメント的に行っています( 変数のパースは {メソッドまたは変数} です )

JavaScript の外部ファイルの読み込み時のキャッシュ制御を行う為、ページが表示された時間を URL に付加しています。
import os
from settings import *
# **************************************
# js 選択
# **************************************
js = "entry.js";
#js = "entry-json.js";

# **************************************
# js キャッシュ用
# **************************************
from datetime import datetime
tm = datetime.now().strftime("%Y%m%d%H%M%S")

# **************************************
# 画面定義
# **************************************
out_client = f"""<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.1/css/bootstrap.css" />

<script src="{js}?{tm}"></script>

<link rel="stylesheet" href="entry.css?{tm}">
</head>
<body>

<div id="head">
	<p class="ttl">
		氏名で検索
	</p>
	<p class="entry">
		<input
			id="cond"
			type="text">
		<input
			class="ml-4 btn btn-success"
			id="btn"
			type="button"
			value="問合せ">

		<a
			class="ml-4 btn btn-info btn-sm"
			href="{os.path.basename(get("base_name"))}">GET 再読み込み</a>	
	</p>
	<p class="line"></p>

	<h4 class="text-danger">{get("check_message")}</h4>

</div>

<iframe id="extend" name="extend" class="iframe-option" src="req/control.py"></iframe>

</body>
</html>"""


print(out_client)


entry.js

1) Bootstrap のボタンの表示位置の調整を行っています。
2) 問い合わせの条件を JavaScript 側で作成して IFRAME の src にセットしています。
$(function(){
	// ***************************
	// ボタン表示位置微調整
	// ***************************
	$( ".btn" ).css({
		"margin-top": "-4px"
	});

	// ***************************
	// IFRAME に問合せを表示
	// ***************************
	$( "#btn" ).click(function() {
		$("#extend").prop("src","req/control.py?nm=" + encodeURIComponent($("#cond").val()));
	});


});


entry.css

Python では、CSS 内に書く記述が一部文字列内でエラーになったので .css として利用しています。
/* ブロックを左右に表示  */
.ttl {
	display: inline-block;
	width: 150px;
}
.entry {
	display: inline-block;
}
.line {
	margin-bottom: 0;
}

/* IFRAMEコントロール用  */
html,body {
	height: 100%;
}

body {
	margin: 0;
}

/* IFRAMEコントロール用  */
#head {
	padding: 16px;
	width: 100%;
	height: 100px;
	background-color: #e0e0e0;
}
iframe {
	display: block;
	margin-left: auto;
	margin-right: auto;
	width: calc( 100% - 3px );
	height: calc( 100% - 100px - 2px );
	border: solid 2px #c0c0c0;
}

control.py ( 問い合わせのエントリポイント )

単独で動作する問い合わせアプリケーションです。GET メソッドで QueryString を受け取って内部の SQL 処理に引き渡します。セキュリティ上の文字列の処理は省略しています。

mysql.connector は、Oracle が提供している Connector/Python を使用します( インストールが必要です ) 
※ autocommit がデフォルトで False なので接続時に True に設定しています

set_field_names メソッドは、settings モジュールで定義されており、画面のフィールド名やフィールド値を参照する為の日本語の定義となります。

sys.path.append('..') は、親フォルダにあるモジュールをインポートするパスをシステムに追加しています。
# **************************************
# 親ディレクトリに参照パス追加
# **************************************
import sys
sys.path.append('..')

# **************************************
# CGI
# **************************************
import cgitb
cgitb.enable()
# **************************************
# MySQL
# **************************************
import mysql.connector

# **************************************
# アプリケーションメソッド
# **************************************
from model import *

# **************************************
# 共有メソッド
# **************************************
from settings import *
set( "base_name", __file__ )

# **************************************
# CGI 初期処理
# **************************************
cgi_init()

# **************************************
# フォームフィールドセット設定
# **************************************
set_field_names( {
	"条件" : "nm",
	"送信ボタン" : "send"
})

# **************************************
# 接続 / 更新兼用
# **************************************
cnn = mysql.connector.connect(
	host='localhost',
	port=3306,
	db='lightbox',
	user='root',
	passwd='',
	charset="utf8",
	autocommit='True')

build_table( cnn )

# **************************************
# 接続解除
# **************************************
cnn.close()

import view


view.py ( 問い合わせ用画面 )

table 部分の行データの埋め込み部分に tbody を明示しているのは、行を jQuery で動的に作成した時に tbody が無いと Bootstrap の css が動作しないからです( 他のテンプレートと差異を少なくする為 )。

一番最後に pplog で、ディクショナリの内容を出力していますが、control.py はエントリポイントなので 変数の参照ができないので、処理の最後として view.py の最後で参照しています( エントリポイントでは、エントリポイントのメモリ空間に最初に使った変数が割り付けられるようです / それ以外の場合は from モジュール import * で直接モジュール変数の参照が可能です )
# **************************************
# 親ディレクトリに参照パス追加
# **************************************
import sys
sys.path.append('..')

#import os
# **************************************
# 共有メソッド
# **************************************
from settings import *
# **************************************
# js 選択
# **************************************
js = "entry.js";
#js = "entry-json.js";

# **************************************
# js キャッシュ用
# **************************************
from datetime import datetime
tm = datetime.now().strftime("%Y%m%d%H%M%S")

# **************************************
# 画面定義
# **************************************
out_client = f"""<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.1/css/bootstrap.css" />

<script src="{js}?{tm}"></script>

<link rel="stylesheet" href="entry.css?{tm}">
</head>
<body>

	<table class="table table-hover">
		<tbody id="tbl">
			{get("lines")}
		</tbody>
	</table>

</body>
</html>"""


print(out_client)

pplog(fld_names)
pplog(val)

entry.css

テーブル表示時のカーソルを常にデフォルトに固定して、テーブルのデータが折り返さないように以下の CSS が追加定義されています
td,th {
	cursor: default!important;
	white-space: pre;
}

body {
	margin: 0;
	padding: 16px;
}


model.py ( テーブル作成部分 )

1) 列データの文字列をまずループで作成しています( SQLを変更するだけで違った問い合わせを表現できます )
2) 列データが完成する毎に、行データを作成して全体の文字列に追加していきます。
3) cnn.cursor(dictionary=True) で、結果をディクショナリ(PHP:連想配列/Ruby:ハッシュ)
import mysql.connector
from settings import *

# ***************************
# テーブル作成
# ***************************
def build_table( cnn ):

	cursor = cnn.cursor(dictionary=True)

	sql = f"""select
		社員コード,
		氏名,
		フリガナ,
		所属,
		性別,
		作成日,
		更新日,
		給与,
		手当,
		管理者,
		DATE_FORMAT(生年月日,'%Y/%m/%d') as 生年月日
		from 社員マスタ
		where 氏名 like '%{forms("条件")}%'"""

	# デバッグ
	log(sql)

	cursor.execute(sql)

	lines  = ""
	for row in cursor:

		if lines == "":
			# タイトル
			for col in row.keys():
				lines += f"<th>{col}</th>"

		cells  = ""
		for col in row.values():
			if col is None:
				cells += f"<td></td>"
			else:
				cells += f"<td>{col}</td>"
	
		lines += f"<tr>{cells}</tr>"
	
	cursor.close()

	set("lines",lines)




関連する記事

Ruby + MySQL + IFRAME + Bootstrap : 問い合せ WEB アプリテンプレート

PHP + MySQL + IFRAME + Bootstrap : 問い合せ WEB アプリテンプレート

CSS の calc 関数を使って、IFRAME を画面下半分にフィットさせる





posted by lightbox at 2018-07-23 10:32 | Python | このブログの読者になる | 更新情報をチェックする

2018年07月11日


MySQL Connector/Python の使用方法概要のまとめ

接続
import mysql.connector

# **************************************
# 接続 / 更新兼用
# **************************************
cnn = mysql.connector.connect(
	host='localhost',
	port=3306,
	db='lightbox',
	user='root',
	passwd='',
	charset="utf8",
	autocommit=True)

# データベース処理

# **************************************
# 接続解除
# **************************************
cnn.close()


MySQLCursor.fetchone() Method
このメソッドはクエリ結果セットの次の行を取得し、単一のシーケンスを返し、使用可能な行がない場合はNoneを返します。 

デフォルトでは返されたタプルはMySQLサーバから返されたデータで構成され、Pythonオブジェクトに変換されます。(カーソルが生のカーソルの場合そのような変換は行われません : 「cursor.MySQLCursorRawクラス」 )

fetchone メソッドは fetchall および fetchmany によって使用されます。 また、カーソルがイテレータとして使用さる場合にも使用されます。

次の例はカーソルをイテレータとして使用し、内部では fetchone  が使用されます。
cursor.execute("SELECT * FROM employees")
for row in cursor:
	print(row)
関数で処理
# ***************************
# 参照
# ***************************
def get_data( cnn ):

	log("get_data")

	cursor = cnn.cursor(dictionary=True)

	sql = f"""select
			社員コード,
			氏名,
			フリガナ,
			所属,
			性別,
			作成日,
			更新日,
			給与,
			手当,
			管理者,
			DATE_FORMAT(生年月日,'%Y/%m/%d') as 生年月日
		from 社員マスタ
		where 社員コード = '{forms("社員コード")}'"""

	log(sql)

	cursor.execute(sql)

	row = cursor.fetchone()

	if row is None:
		set("check_message", "データが存在しません")
		return False

	pplog(row)

	forms("氏名", row["氏名"])
	forms("フリガナ", row["フリガナ"])
	forms("給与", row["給与"])
	forms("生年月日", row["生年月日"])

	cursor.close()

	return True

pplog の結果
{'フリガナ': 'ウラオカ トモヤ',
 '作成日': datetime.datetime(2005, 9, 12, 0, 0),
 '性別': 0,
 '所属': '0003',
 '手当': 9000,
 '更新日': datetime.datetime(2005, 11, 28, 0, 0),
 '氏名': '浦岡 友也',
 '生年月日': '2001/01/10',
 '社員コード': '0001',
 '管理者': None,
 '給与': 80000}
MySQLCursor.execute() Method
このメソッドは指定されたデータベース操作(クエリまたはコマンド)を実行します。

タプルまたはディクショナリ(PHP:連想配列/Ruby:ハッシュ)の値は、SQL文字列内の変数にバインドされます。 

変数を%sまたは%(name)s として埋め込んで使用します。 
▼ 例
select_stmt = "SELECT * FROM employees WHERE emp_no = %(emp_no)s"
cursor.execute(select_stmt, { 'emp_no': 2 })

▼ 非推奨
execute は、multiがTrueの場合はイテレータを返します。
Pythonでは、単一の値を含むタプルにカンマを含める必要があります。
たとえば ('abc') はスカラとして評価され、('abc',)はタプルとして評価されます。
print( type(('abc')) )
print( type(('abc',)) )

▼ 処理結果
<class 'str'>
<class 'tuple'>
関数で処理
# ***************************
# 更新
# ***************************
def update_data( cnn ):
	log("update_data")

	cursor = cnn.cursor()

	# 文字列複数行連結
	sql = f"""update 社員マスタ set 
		氏名 = %s,
		フリガナ=%s,
		給与 = %s,
		生年月日 = %s
	where 社員コード = '{forms("社員コード")}'"""

	log( sql )

	try:
		data = (forms("氏名"), forms("フリガナ"), int(forms("給与")) , forms("生年月日") )
	
		log( data )
	
		cursor.execute(sql, data)
	except:
		error_info = str(sys.exc_info()[1])
		set("check_message", "更新に失敗しました : " + error_info)
		log(str(sys.exc_info()[0]))
		log(error_info)
		return False

	cursor.close()

	return True


sys.exc_info()
現在処理中の例外を示す 3 個のタプルを返します。

例外が発生している場合、(type, value, traceback) を返します。type は処理中の例外のクラス(BaseException のサブクラス)を示します。value は例外インスタンス (例外の型のインスタンス) を返します(エラーメッセージはここに格納されています)
▼ エラーメッセージ例(MySQL)
1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'aaa' at line 1
ログ関数
import pprint

# **************************************
# ログ出力
# **************************************
def log( message ):
	with open('debug.log', 'a') as f:
		print(message,end='\n',file=f)
	
# **************************************
# ログ出力( pprint )
# **************************************
def pplog( obj ):

	with open("debug.log", "a") as f:
		pprint.pprint(obj, stream=f)






posted by lightbox at 2018-07-11 13:39 | Python | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり