今の勤務先は社員が出社すると社内のチャットで勤務開始を連絡している。
あくまでもチャットなので全員の出勤状態を見たい時はチャットを遡って見ないといけない。
オフィスに行っていた頃は行動予定表というホワイトボードがあって名前の書いてあるマグネットを貼っていたので、WEBで使える物があれば良いと思った。
この記事ではWindowsOSのPCでpythonを使ったCGIとMySQL接続によるWEBサイトの作成とレンタルサーバへのアップロードを行いWEBサイトの公開までを紹介しています。
完成したサイト
これです。
見た目も使い方もシンプルです。
- グループを作成して勤怠ボードを作成
- メンバーを登録
- URLをメンバーに共有
これだけで毎日の勤怠を連絡したり確認したりできるようになります。
勤怠と言っても今仕事してるのか、まだ寝てるのか?くらいしかわかりませんが、
出勤確認をチャットでやるより分かりやすいかなと思いました。
パソコンをWEBサーバにして開発を始める
pythonのインストール
pythonは前からPCにインストールしていたのでこの辺りのサイトを参考にしてください。
MySQL操作用モジュールをインストール
コマンドプロンプトで実行します。
python -m pip install --upgrade pip
pythonでHTTPサーバを起動
python -m http.server 8080 --cgi
実行したフォルダにindex.htmlファイルを置けば読み込めます。
またCGI用の.pyファイルはcgi-binフォルダを作成してそこに保存します。
MySQLのインストール
MySQLのインストールは初めてだったのでこちらのサイトを参考にさせてもらいました。
以下は実際に使ったDB作成コマンドです。
DB接続
コマンドプロンプトで実行します。
mysql -u root -p
DBを作る
kiteruというデータベース名にします。
create database kiteru;
DBを選択する
use kiteru;
テーブルを作る
boardとuserという2つのテーブルを作ります。
まずは1つ目boardテーブルの作成。
create table board(bid varchar(33) not null primary key, name varchar(100), memo varchar(300));
boardについてはbid、name、memoの3つの列を作成。
- bidのvarchar(33)は”データの型”を指定していて文字で最大33文字までという指定です。
”データの型”はどの列でも必ず指定する必要があります。 - bidのnot nullは必ず値を入れる必要があるという指定です。
- bidのprimary keyはbidの列は重複する値を入れないという指定です。
nameとmemoもvarcharで文字を指定していますが最大文字数を変えています。
2つ目のuserテーブルの作成。
create table user(uid int AUTO_INCREMENT, bid varchar(33) not null, name varchar(30), status varchar(10), comment varchar(100), primary key(uid,bid));
userについてはuid、bid、name、status、commentの5つの列を作成。
- uidのintは”データの型”を指定していて数字が入る指定です。
- uidのAUTO_INCREMENTは若い番号から自動で入力するという指定です。
最後のprimary key(uid,bid)はuidとbidのセットで重複する値を入れないという指定です。
普通1つのキーのみに設定しますが、なんとなくセットにしました。
ちなみにphpMyAdminというCMSを使えばMySQLをブラウザを追加ったGUIで操作できますが、phpMyAdminのインストールや設定が面倒だったのでCUIで済ませています。
WEBサイトを組み立てる
WEBサイト制作には下記ソフトを使いました。
- メモ帳
- GoogleChrome
- コマンドプロンプト
メモ帳でpythonのプログラムを書いたり、HTMLを書いたりします。
GoogleChromeでHTMLの表示やpythonの表示を確認します。
閲覧だけならIEでも良いですが、GoogleChromeはWEBページ上を右クリックしたときに表示される「検証」というツールが超高性能なので必須レベルです。
コマンドプロンプトはpythonのエラーを確認したり、MySQLのデータベースの状態を確認したりします。
今回作ったのは11ファイルです。
トップページ
<html lang="ja"> <head> <meta http-equiv="content-type" charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script> <style> .modal_view_useradd,.modal_view_userchange,.modal_view_commentchange {display: none;position: fixed;z-index: 1;left: 0;top: 0;height: 100%;width: 100%;background-color: rgba(0,0,0,0.5);} .modal-content {background-color: #f4f4f4;width: 80%;margin:auto;padding: 1rem;auto;box-shadow: 0 5px 8px 0 rgba(0,0,0,0.2),0 7px 20px 0 rgba(0,0,0,0.17);animation-name: modalopen;animation-duration: 1s;} </style> <style> .header{ border-bottom: 1px solid rgb(229, 229, 229); width: 100%; padding: 15px 5px; background-color: #fff; } .maincontents{ width: 100%; background-color: #f5fdfd; } .footer{ border-top: 1px solid rgb(229, 229, 229); width: 100%; padding: 15px 5px; background-color: #ddd; } .bg-f5{ background-color: #f5f5f5; } .logo{ width: 250px; } </style> <title>きてる?</title> </head> <body> <div class="header"> <img src="/img/logo.png" alt="ロゴ画像" class="logo"> <br> <span class="small text-muted">無料で使える勤怠管理ツール</span> </div> <div class="container"> <div class="row"> <div class="col-md-8 offset-md-2"> <h2>グループを作る</h2> <form action="/boardmake.py" method="post"> <div class="form-group"> <label for="name">グループ名</label> <input name="name" id="name" class="form-control"> </div> <div class="form-group"> <label for="memo">メモ</label> <textarea name="memo" id="memo" class="form-control"></textarea> </div> <div class="d-grid gap-2 col-6 mx-auto mt-2"> <button class="btn btn-success">作成</button> </div> </form> </div> </div> </div> <div id="footer" class="footer"> <span>©2022® - きてる?</span> </div> <script type="text/javascript" src="/js/footerFixed.js"></script> </body> </html>
勤怠ボード作成画面
トップページで指定したグループ名をデータベースのboardに書き込みます。
#!/usr/local/bin/python3.7 # coding:utf-8 import cgitb import sys ,io import string import random import cgi import common_head import common_header import common_footer import common_gotoindex import mysql.connector sys.stdin = open(sys.stdin.fileno(), 'r', encoding='UTF-8') sys.stdout = open(sys.stdout.fileno(), 'w', encoding='UTF-8') sys.stderr = open(sys.stderr.fileno(), 'w', encoding='UTF-8') cgitb.enable() def GetRandomStr(num): # 英数字をすべて取得 dat = string.digits + string.ascii_lowercase + string.ascii_uppercase # 英数字からランダムに取得 return ''.join([random.choice(dat) for i in range(num)]) bid = GetRandomStr(33) # デバッグ用 cgitb.enable() form = cgi.FieldStorage() form_check = 0 # formでの変数有無チェック if "name" not in form: common_gotoindex.gotoindex_print() sys.exit() else: form_name = form["name"].value if "memo" not in form: form_memo = "" else: form_memo = form["memo"].value cnx = None try: cnx = mysql.connector.connect( #local user='root', # ユーザー名 password='password', # パスワード host='localhost', # ホスト名(IPアドレス) database='kiteru' # データベース名 ) cursor = cnx.cursor() sql = (''' INSERT INTO board (bid, name, memo) VALUES (%s, %s, %s) ''') data = [ (bid, form_name, form_memo) ] cursor.executemany(sql, data) cnx.commit() # print(f"{cursor.rowcount} records inserted.") cursor.close() except Exception as e: errr = e print(f"Error Occurred: {e}") finally: if cnx is not None and cnx.is_connected(): cnx.close() #print("Content-Type: text/html; charset=UTF-8\r\n") #print("<html>") common_head.head_print() print("<body>") common_header.header_print() print ('<div class="container">') print ('<div class="row">') print ("<br>") print ('<span class="">%s<span class="small">を作成しました。</span></span>' %form_name) print ("<br>") print ("<input type=""text"" name=""memo"" id=""memo"" size=""100"" value=""/?bid=""%s"">" %bid) print (r'<form action="/boardview.py" method="GET">') print ("<input type=""hidden"" name=""bid"" id=""bid"" size=""100"" value=""%s"">" %bid) print ('<div class="d-grid gap-2 col-6 mx-auto mt-2">') print ('<button class="btn btn-success">勤怠ボードを開く</button>') print ('</div>') print (r'</form>') print ("</div>") print ("</div>") #フッターを別ファイルで読み込み common_footer.footer_print() print ("</body></html>")
勤怠管理システムのメインの画面
データベースのboardとuserを読み込んで勤怠ボードを表示します。
ここから出勤や退勤の操作を行えます。
#!/usr/local/bin/python3.7 # coding:utf-8 # MySQL操作モジュールの読み込み import cgitb import sys ,io import mysql.connector as mydb import cgi import common_head import common_header import common_footer import common_gotoindex sys.stdin = open(sys.stdin.fileno(), 'r', encoding='UTF-8') sys.stdout = open(sys.stdout.fileno(), 'w', encoding='UTF-8') sys.stderr = open(sys.stderr.fileno(), 'w', encoding='UTF-8') #printの文字コード指定 #sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') # UTF-8に form = cgi.FieldStorage() # formでの変数有無チェック if "bid" not in form: form_check = 1 common_gotoindex.gotoindex_print() sys.exit() #フォームからbid取得 bid = form["bid"].value #フォーム文字数チェック if len(bid) != 33: common_gotoindex.gotoindex_print() sys.exit() pass # コネクションの作成 try: conn = mydb.connect( #local user='root', # ユーザー名 password='password', # パスワード host='localhost', # ホスト名(IPアドレス) database='kiteru' # データベース名 ) cur = conn.cursor() #DBから取得 cur.execute("SELECT name,status,comment,uid FROM user WHERE bid =%s",(bid,)) #エラー処理 except mydb.Error as e: common_gotoindex.gotoindex_print() sys.exit() # 全てのデータを取得 rows = cur.fetchall() #print (len(rows)) #if len(rows) == 0: # common_gotoindex.gotoindex_print() # sys.exit() # pass #タイトル用にグループ名取得 cur = conn.cursor() cur.execute("SELECT name,memo FROM board WHERE bid =%s",(bid,)) rowsboard = cur.fetchall() #headを別ファイルで読み込み common_head.head_print() #BODY print ("<body>") #ヘッダーを別ファイルで読み込み common_header.header_print() print (r'<div class="container">') print ('<div class="row">') print ('<div class="border-start border-success border-5">') print ("<h2>") print (rowsboard[0][0]) print ("</h2>") print (r'<span class="small text-muted">%s</span>' %rowsboard[0][1]) print ("</div>") #テーブル print (r'<div class="table-responsive">') print (r'<table class="table table-hover table-bordered">') print ('<thead class="thead-table small text-muted bg-f5">') print ("<tr>") print ('<th scope="col"><div class="text-nowrap">名前</div></th>') print ('<th scope="col"><div class="text-nowrap">勤怠</div></th>') print ('<th scope="col"><div class="text-nowrap">変更ボタン</div></th>') print ('<th scope="col"><div class="text-nowrap">コメント</div></th>') print ("</tr>") print ("</thead>") print ("<tbody>") rownumber = 0 for row in rows: print ("<tr>") colnumber = 0 for data in row: if colnumber == 2: print ("<td>") #行番号をIDに設定して【kinntai_button】と【kinntai_view】のIDを合わせる print ('<div class="text-nowrap">') print ('<input type="button" id="%s" class="kinntai_button btn btn-light shadow-sm" value="未出勤"></input>' %row[len(row)-1]) print ('<input type="button" id="%s" class="kinntai_button btn btn-light shadow-sm" value="出勤"></input>' %row[len(row)-1]) print ('<input type="button" id="%s" class="kinntai_button btn btn-light shadow-sm" value="休憩"></input>' %row[len(row)-1]) print ('<input type="button" id="%s" class="kinntai_button btn btn-light shadow-sm" value="退勤"></input>' %row[len(row)-1]) print ('</div>') print ("</td>") print ("<td>") print ('<span id="%s" class="comment_view modal_button_commentchange text-decoration-underline">%s</span>' %(row[len(row)-1],data)) print ("</td>") elif colnumber == 1: print ("<td>") print ('<span id="%s" class="kinntai_view text-nowrap">%s</span>' %(row[len(row)-1],data)) print ("</td>") elif colnumber == 0: print ('<th scope="row">') print ('<span id="%s" class="name_view modal_button_userchange text-nowrap">%s</span>' %(row[len(row)-1],data)) print ("</th>") elif colnumber == 3: pass else: print ("<td>") print(data) print ("</td>") colnumber+=1 print ("</tr>") rownumber+=1 print ("</tbody>") print ("</table>") print ("</div>") print ('<div class="col-6">') print ('<input type=button id="user_add" class="modal_button_useradd btn btn-success" value="メンバーを追加する">') print ('</div>') print ('</div>') #メンバー追加フォーム print (r'<div id="user_add" class="modal_view_useradd">') print (r'<div class="modal-content">') print (r'<h2>追加するメンバーを入力してください。</h2>') print (r'<form action="/usermake.py" method="post">') print (r'<div class="form-group">') print (r'<label for="name">名前</label>') print (r'<input name="name" id="name" class="form-control">') print ('<input type="hidden" name="bid" id="bid" value="%s" class="form-control">' %bid) print (r'</div>') print (r'<div class="d-grid gap-2 col-6 mx-auto mt-2">') print (r'<button class="btn btn-success">追加する</button>') print (r'</div>') print (r'</form>') print (r'</div>') print (r'</div>') #名前変更フォーム print (r'<div id="user_change" class="modal_view_userchange">') print (r'<div class="modal-content">') print (r'<h2>変更する名前を入力してください。</h2>') print (r'<form action="/userchange.py" method="get">') print (r'<div class="form-group">') print (r'<label for="name">名前</label>') print (r'<input name="name" id="name" class="name_form form-control" value="">') print ('<input type="hidden" name="uid" id="id" class="name_form_uid" value="">') print ('<input type="hidden" name="bid" id="bid" value="%s">' %bid) print (r'</div>') print (r'<div class="d-grid gap-2 col-6 mx-auto mt-2">') print (r'<button class="btn btn-success">変更する</button>') print (r'</div>') print (r'</form>') print (r'</div>') print (r'</div>') #コメント入力 print (r'<div id="comment_change" class="modal_view_commentchange">') print (r'<div class="modal-content">') print (r'<h2>コメント</h2>') print (r'<form action="/commentchange.py" method="post">') print (r'<div class="form-group">') print (r'<label for="name">コメント</label>') #print (r'<input name="comment" id="comment" class="comment_form form-control" value="">') print (r'<textarea name="comment" id="comment" class="comment_form form-control" value=""></textarea>') print ('<input type="hidden" name="uid" id="id" class="comment_form_uid" value="">') print ('<input type="hidden" name="bid" id="bid" value="%s">' %bid) print (r'</div>') print (r'<div class="d-grid gap-2 col-6 mx-auto mt-2">') print (r'<button class="btn btn-success">更新</button>') print (r'</div>') print (r'</form>') print (r'</div>') print (r'</div>') print (r'<script>') print (r"document.addEventListener('DOMContentLoaded',function(){") #モーダル操作 #[メンバー追加]画面外で閉じる print (r" var mvua = document.querySelectorAll('.modal_view_useradd')[0];") print (r" addEventListener('click',function(e){") print (r" if (e.target == mvua){") print (r" mvua.style.display = 'none';") print (r' }') print (r' },false);') #[メンバー追加]表示する print (r" var btns = document.querySelectorAll('.modal_button_useradd');") print (r' for(var i = 0; i < btns.length; i++){') print (r" btns[i].addEventListener('click',function(){") print (r" var views = document.querySelectorAll('.modal_view_useradd');") print (r' for(var j = 0; j < views.length; j++){') print (r" views[j].style.display = 'block';") print (r' }') print (r' },false);') print (r' }') #[メンバー変更]画面外で閉じる print (r" var mvuc = document.querySelectorAll('.modal_view_userchange')[0];") print (r" addEventListener('click',function(e){") print (r" if (e.target == mvuc){") print (r" mvuc.style.display = 'none';") print (r' }') print (r' },false);') #[メンバー変更]表示する print (r" var btns = document.querySelectorAll('.modal_button_userchange');") print (r' for(var i = 0; i < btns.length; i++){') print (r" btns[i].addEventListener('click',function(){") print (r" var views = document.querySelectorAll('.modal_view_userchange');") print (r' for(var j = 0; j < views.length; j++){') print (r" views[j].style.display = 'block';") #print (r" select_id = this.id;") #print (r" var set_uid = document.querySelectorAll('name_form_uid');") #print (r" set_uid.value = select_id") print (r" var set_uid = document.querySelectorAll('name_form_uid');") print (r" set_uid.value = this.id") print (r' }') print (r' },false);') print (r' }') #[コメント]画面外で閉じる print (r" var mvcc = document.querySelectorAll('.modal_view_commentchange')[0];") print (r" addEventListener('click',function(e){") print (r" if (e.target == mvcc){") print (r" mvcc.style.display = 'none';") print (r' }') print (r' },false);') #[コメント]表示する print (r" var btns = document.querySelectorAll('.modal_button_commentchange');") print (r' for(var i = 0; i < btns.length; i++){') print (r" btns[i].addEventListener('click',function(){") print (r" var views = document.querySelectorAll('.modal_view_commentchange');") print (r' for(var j = 0; j < views.length; j++){') print (r" views[j].style.display = 'block';") print (r" var set_uid = document.querySelectorAll('modal_view_commentchange');") print (r" set_uid.value = this.id") print (r' }') print (r' },false);') print (r' }') #勤怠表示変更用 print (r" var btns = document.querySelectorAll('.kinntai_button');") print (r' for(var i = 0; i < btns.length; i++){') print (r" btns[i].addEventListener('click',function(){") print (r" var outs = document.querySelectorAll('.kinntai_view');") print (r' for(var j = 0; j < outs.length; j++){') print (r' if (outs[j].id == this.id){') #勤怠表示のみ変更 print (r' outs[j].textContent = this.value') #勤怠DB変更 #URLのGET情報を取得 print (" var url = new URL(window.location.href);") print (" var params = url.searchParams;") #送信するGETパラメータ準備 print (" let getparams = new URLSearchParams();") print (" getparams.set('uid', this.id);") print (" getparams.set('status', this.value);") print (" getparams.set('bid', params.get('bid'));") print (" fetch(`/statuschange.py?` + getparams.toString())") print (r' .then((res)=>{') print (r' return( res.text() );') print (r' })') print (r' .then((text)=>{') print (r' console.log(text);') print (r' inputElem.value = text;') print (r' setCurrentValue(inputElem.value);') print (r' })') print (r' .catch((error)=>{') #print (r' console.error(error);') print (r' });') print (r' }') print (r' }') print (r' },false);') print (r' }') #名前選択用 print (r" var btns = document.querySelectorAll('.name_view');") print (r' for(var i = 0; i < btns.length; i++){') print (r" btns[i].addEventListener('click',function(){") print (r" var outs = document.querySelectorAll('.name_form');") print (r' for(var j = 0; j < outs.length; j++){') #クリックした名前とUIDを名前フォームへ入れる print (r' outs[j].value = this.textContent') print (r' outs[j].id = this.id') print (r' }') print (r" var outs = document.querySelectorAll('.name_form_uid');") print (r' for(var j = 0; j < outs.length; j++){') #UIDを名前フォームのUIDへ入れる print (r' outs[j].value = this.id') print (r' }') print (r' },false);') print (r' }') #コメント選択用 print (r" var btns = document.querySelectorAll('.comment_view');") print (r' for(var i = 0; i < btns.length; i++){') print (r" btns[i].addEventListener('click',function(){") print (r" var outs = document.querySelectorAll('.comment_form');") print (r' for(var j = 0; j < outs.length; j++){') #クリックした名前とUIDを名前フォームへ入れる print (r' outs[j].value = this.textContent') print (r' outs[j].id = this.id') print (r' }') print (r" var outs = document.querySelectorAll('.comment_form_uid');") print (r' for(var j = 0; j < outs.length; j++){') #UIDを名前フォームのUIDへ入れる print (r' outs[j].value = this.id') print (r' }') print (r' },false);') print (r' }') print (r'},false);') print (r'</script>') print ('</div>') print ('</div>') #フッターを別ファイルで読み込み common_footer.footer_print() print ("</body></html>")
メンバー作成処理
boardview.pyにあるメンバー作成ボタンを押すと呼び出されます。
userに新しいメンバーを追加します。
#!/usr/local/bin/python3.7 # coding:utf-8 import mysql.connector import cgi # デバッグ用 import cgitb cgitb.enable() form = cgi.FieldStorage() form_check = 0 # formでの変数有無チェック if "name" not in form: form_check = 1 cnx = None try: cnx = mysql.connector.connect( #local user='root', # ユーザー名 password='password', # パスワード host='localhost', # ホスト名(IPアドレス) database='kiteru' # データベース名 ) cursor = cnx.cursor() sql = (''' INSERT INTO user (uid, bid, name) VALUES (%s, %s, %s) ''') data = [ (0, form["bid"].value, form["name"].value) ] cursor.executemany(sql, data) cnx.commit() # print(f"{cursor.rowcount} records inserted.") cursor.close() except Exception as e: print(f"Error Occurred: {e}") finally: if cnx is not None and cnx.is_connected(): cnx.close() print ("Content-Type: text/html") print () print ("<HTML><HEAD></HEAD><BODY>") print (r'<script>') #呼び出し元のページを開きなおす print ("location.href = '/boardview.py?bid=%s';" %form["bid"].value) print (r'</script>') #print (form["name"].value) print ("</BODY></HTML>")
勤怠変更処理
出勤、休憩、退勤ボタンが押された時に呼び出されます。
userのstatusを変更します。
#!/usr/local/bin/python3.7 # coding:utf-8 import mysql.connector import cgi # デバッグ用 import cgitb cgitb.enable() form = cgi.FieldStorage() form_check = 0 # formでの変数有無チェック if "status" not in form: form_check = 1 cnx = None try: cnx = mysql.connector.connect( #local user='root', # ユーザー名 password='password', # パスワード host='localhost', # ホスト名(IPアドレス) database='kiteru' # データベース名 ) cursor = cnx.cursor() sql = (''' UPDATE user set status= (%s) WHERE uid= (%s) ''') data = [ (form["status"].value,form["uid"].value) ] cursor.executemany(sql, data) cnx.commit() # print(f"{cursor.rowcount} records inserted.") cursor.close() except Exception as e: print(f"Error Occurred: {e}") finally: if cnx is not None and cnx.is_connected(): cnx.close() print ("Content-Type: text/html") print () print ("<HTML><HEAD></HEAD><BODY>") print (r'<script>') #呼び出し元のページを開きなおす print ("location.href = '/boardview.py?bid=%s';" %form["bid"].value) print (r'</script>') #print (form["name"].value) print ("</BODY></HTML>")
メンバー名前変更処理
boardview.pyにあるメンバー名変更ボタンを押すと呼び出されます。
userのメンバー名を変更します。
#!/usr/local/bin/python3.7 # coding:utf-8 import mysql.connector import cgi # デバッグ用 import cgitb cgitb.enable() form = cgi.FieldStorage() form_check = 0 # formでの変数有無チェック if "name" not in form: form_check = 1 cnx = None try: cnx = mysql.connector.connect( #local user='root', # ユーザー名 password='password', # パスワード host='localhost', # ホスト名(IPアドレス) database='kiteru' # データベース名 ) cursor = cnx.cursor() sql = (''' UPDATE user set name= (%s) WHERE uid= (%s) and bid=(%s) ''') data = [ (form["name"].value,form["uid"].value,form["bid"].value) ] cursor.executemany(sql, data) cnx.commit() # print(f"{cursor.rowcount} records inserted.") cursor.close() except Exception as e: print(f"Error Occurred: {e}") finally: if cnx is not None and cnx.is_connected(): cnx.close() print ("Content-Type: text/html") print () print ("<HTML><HEAD></HEAD><BODY>") print (r'<script>') #呼び出し元のページを開きなおす print ("location.href = '/boardview.py?bid=%s';" %form["bid"].value) print (r'</script>') #print (form["name"].value) print ("</BODY></HTML>")
メンバーコメント変更処理
boardview.pyにあるコメント変更ボタンを押すと呼び出されます。
userのコメントを変更します。
#!/usr/local/bin/python3.7 # coding:utf-8 import mysql.connector import cgi # デバッグ用 import cgitb cgitb.enable() form = cgi.FieldStorage() form_check = 0 # formでの変数有無チェック if "comment" not in form: form_check = 1 cnx = None try: cnx = mysql.connector.connect( #local user='root', # ユーザー名 password='password', # パスワード host='localhost', # ホスト名(IPアドレス) database='kiteru' # データベース名 ) cursor = cnx.cursor() sql = (''' UPDATE user set comment= (%s) WHERE uid= (%s) and bid=(%s) ''') data = [ (form["comment"].value,form["uid"].value,form["bid"].value) ] cursor.executemany(sql, data) cnx.commit() # print(f"{cursor.rowcount} records inserted.") cursor.close() except Exception as e: print(f"Error Occurred: {e}") finally: if cnx is not None and cnx.is_connected(): cnx.close() print ("Content-Type: text/html") print () print ("<HTML><HEAD></HEAD><BODY>") print (r'<script>') #呼び出し元のページを開きなおす print ("location.href = '/boardview.py?bid=%s';" %form["bid"].value) print (r'</script>') #print (form["name"].value) print ("</BODY></HTML>")
HTMLヘッド表示処理
boardmake.pyとboardview.pyから呼び出されてヘッドを出力します。
ヘッドの主な内容はHTMLの表示を見やすくするCSSやJavaScriptの読み込みです。
どのページも同じ内容なので共通情報として作成しました。
#!/usr/local/bin/python3.7 # coding:utf-8 def head_print(): print("Content-Type: text/html; charset=UTF-8\r\n") #この空行消さないで!! print () print ('<html lang="ja">') headtext = ''' <head> <meta http-equiv="content-type" charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script> <style> .modal_view_useradd,.modal_view_userchange,.modal_view_commentchange {display: none;position: fixed;z-index: 1;left: 0;top: 0;height: 100%;width: 100%;background-color: rgba(0,0,0,0.5);} .modal-content { background-color: #f4f4f4; width: 80%; margin:auto; margin-top:4rem; padding: 1rem; box-shadow: 0 5px 8px 0 rgba(0,0,0,0.2),0 7px 20px 0 rgba(0,0,0,0.17); animation-name: modalopen; animation-duration: 1s; } </style> <style> .header{ border-bottom: 1px solid rgb(229, 229, 229); width: 100%; padding: 15px 5px; background-color: #fff; } .maincontents{ width: 100%; background-color: #f5fdfd; } .footer{ border-top: 1px solid rgb(229, 229, 229); width: 100%; padding: 15px 5px; background-color: #ddd; } .bg-f5{ background-color: #f5f5f5; } .logo{ width: 250px; } </style> <title>きてる?</title> </head>''' print (headtext)
ヘッダー表示処理
boardmake.pyとboardview.pyから呼び出されてヘッダーを出力します。
どのページも同じ内容なので共通情報として作成しました。
#!/usr/local/bin/python3.7 # coding:utf-8 def header_print(): header = ''' <div class="header"> <img src="/img/logo.png" alt="ロゴ画像" class="logo"> <br> <span class="small text-muted">無料で使える勤怠管理ツール</span> </div> ''' print (header)
フッター表示処理
boardmake.pyとboardview.pyから呼び出されてフッターを出力します。
どのページも同じ内容なので共通情報として作成しました。
#!/usr/local/bin/python3.7 # coding:utf-8 def footer_print(): footer = ''' <div id="footer" class="footer"> <span>©2022® - きてる?</span> </div> <script type="text/javascript" src="/js/footerFixed.js"></script> ''' print (footer)
トップページに戻る処理
エラーや予定外の場合にトップページに戻すJavaScriptの書かれたHTMLを出力します。
#!/usr/local/bin/python3.4 def gotoindex_print(): print ("Content-Type: text/html") print () # print ('<HTML lang="ja"><HEAD><meta http-equiv="content-type" charset="UTF-8"></HEAD><BODY>SQLぶっぶー') print (r'<script>') print ("location.href = '/';") print (r'</script>') print ("</BODY></HTML>")
インターネットに公開する!
ロリポップでレンタルサーバの無料体験をはじめる
ライトプランの下にある「10日間無料で体験」から登録します。
最初何も考えずに一番安いエコノミーで登録しましたが、データベースをサポートしていなかったのでライトプランに後から切り替えました。
無料体験中はプラン変更が自由なので便利です。
レンタルサーバへファイルアップロード
パソコン上で動いていたファイルをそのままアップロードしただけでは上手く動かなかったので、メモがてら残します。
サーバ管理画面から「サーバの管理・設定」「ロリポップ!FTP」を選択
ログインしたらhtmlとpyのファイルをアップロード
1.ファイルパスの「cgi-bin」を削除する
/cgi-binに置いた.pyファイルをインポートする事ができませんでした。
こんなディレクトリだとします。
root/
├boardview.py
└cgi-bin/
└common_head.py
root直下にある「boardview.py」から「cgi-bin/common_head.py」をインポートしようとしましたが、上手くいきませんでした。
仕方ないですが、全てroot/に置いて使う事にしました。
root/
├boardview.py
└common_head.py
こうするとインポートもできました。
なので各ファイルに記述している「/cgi-bin」を消します。
2.コードの最初におまじないを書き込む
#!/usr/local/bin/python3.7 # coding:utf-8 import sys ,io sys.stdin = open(sys.stdin.fileno(), 'r', encoding='UTF-8') sys.stdout = open(sys.stdout.fileno(), 'w', encoding='UTF-8') sys.stderr = open(sys.stderr.fileno(), 'w', encoding='UTF-8')
最初の「#!/usr/local/bin/python3.7」はロリポップの環境によって変わるので個別に確認する必要がある。
この赤いところの「lit011」です。
あとは公式のページで言語ごとのパスを確認します。
3.改行コードをLFに変更
サクラエディタを使ってファイルを保存する画面で右下にある改行コードで「LF」を選択して保存します。
4.BINARYでアップロードする
アップロードを選択
BINARYを指定
「ファイルを選択する」を押す
ファイル選択画面が出るので選択してから「アップロードする」を押す
5.文字コードをUTF-8、権限を700へ変更する
「保存する」を押す
これでChromeからhttps://kiteru1.pigboat.jp/へアクセスすると表示を確認できます。
HTML直書きのindex.htmlは問題なく表示されると思いますが、.pyのファイルは設定を誤ると正しく表示されません。
「CGIもしくはSSIが正しく動作していません。」という画面が何度もでてきてうんざりします。
もう見たくないですが画面を貼っておきます。
レンタルサーバのデータベース設定
WEBブラウザでデータベースの作成や管理ができるphpMyAdminでやります。
「サーバの管理・設定」「データベース」を選択すると、接続情報とphpMyAdminの接続ページが表示されます。
「phpMyAdminを開く」を押します。
https://mysqladmin.lolipop.jp/pma/tbl_structure.php
ログイン画面です。
上に表示されている情報を入力すれば入れます。
テーブルの作成
画面左のツリーで自身で設定したデータベース名を選択
画面右に「テーブルを作成」が出てくるので名前とカラム数を入れて作成します。
boardは既に作っているので、画面では名前は「board1」でカラムは「3」です。
テーブルの内容を設定していきます。
- 名前は上から「bid,name,memo」
- データ型は全て文字列なので「VARCHAR」
- 長さは上から「33,100,300」
- NULLはbidのみチェック
- インデックスはbidのみPRIMARY
これで「保存する」を押すとテーブルが完成します。
同じ要領でuserテーブルも作成します。
これでデータベースへ接続する全てのpythonファイルが動作します。
Pythonはどうやって覚える?
プログラムのコードが読めないと、参考にしているコードの理解や自分でプログラムを書くときに時間がかかってしまいます。そんな時は基礎から学ぶと応用もできるようになり自分の作りたいプログラムがスムーズに作れるようになります。
無料でやるならネット上の学習サイトを参考にするといいです。
こちらはPythonの基礎から説明している無料のWEBサイトになります。
>>基礎から学べるWEBサイトを見てみる
過去にネットでの学習でつまずいた方には専門書をオススメします。
本なら基礎から学べる事はもちろん、読者視点で分かりやすい解説になっています。
一人で集中してコツコツ進めたい方は書籍を試してください。
本を読んでもわからない所が多すぎたり、誰かに質問したい場合はオンラインスクールを見てみましょう。
カウンセリングから目的に応じた学習プランと教材を提供してくれるので、プログラミングの敷居がとても低くなります。
誰かに相談できるのは心強いです。
>>オンラインスクールを無料体験で始める