Flask框架(二)
一 session操作
cookie是客戶端瀏覽器的快取,而session是服務端伺服器的快取
Session 物件儲存特定使用者會話所需的屬性及配置資訊。這樣,當用戶在應用程式的 Web 頁之間跳轉時,儲存在 Session 物件中的變數將不會丟失,而是在整個使用者會話中一直存在下去。當用戶請求來自應用程式的Web 頁時,如果該使用者還沒有會話,則 Web 伺服器將自動建立一個 Session 物件。當會話過期或被放棄後,伺服器將終止該會話。Session 物件最常見的一個用法就是儲存使用者的首選項。
直接看程式碼:
from flask import Flask, session import random app = Flask(__name__) app.config['SECRET_KEY'] = random._urandom(24) # 設定是24位的字元, 每次執行伺服器的secret_key都是不同的, # 伺服器重啟後會清除上一次儲存的session資訊值; # 設定session值; @app.route('/') def index(): # 如何設定session的key-value值 session['name'] = 'westos' return "hello world" @app.route('/get/') def get(): # 如何獲取? return session.get('name') @app.route('/delete/') def delete(): # 如何刪除? print(session.get('name')) session.pop('name') print(session.get('name')) return 'delete' app.run()
執行結果:
index頁面:
get頁面:
可以看到增加的 name 的 value 值 westos
delete頁面:
這裡看到的不太明顯
可以看到在沒執行 delete 刪除任務前,westos 是存在的,執行之後,變為 None
二 將使用者登陸資訊與資料庫繫結
要求將使用者登陸時的資訊,傳送至後臺與資料庫進行比對,來判斷使用者是否可登陸
import pymysql from config import DB # 1. 建立連線 conn = pymysql.connect( host=DB.HOST, user = DB.USER, passwd = DB.PASSWD, port = DB.PORT, db = DB.DBNAME, ) cur = conn.cursor() def isUserExist(username): """判斷使用者名稱是否存在""" sqli = "select * from users where name='%s'" %(username) res = cur.execute(sqli) # res返回的是sql語句查詢結果的個數; # 如果為0, 沒有查到。 if res == 0: return False else: return True def isPasswdOk(username, passwd): sqli = "select * from users where name='%s' and passwd='%s'" %( username, passwd) res = cur.execute(sqli) if res == 0 : return False else: return True def addUser(username, passwd): """使用者註冊時, 新增資訊到資料庫中""" sqli = "insert into users(name, passwd) values('%s', '%s')" %( username, passwd) try: res = cur.execute(sqli) conn.commit() except Exception as e: conn.rollback() return e # # cur.close() # conn.close() if __name__ == "__main__": addUser('root', 'root') print(isUserExist('root')) print(isPasswdOk('root', 'root'))
config檔案:
class DB:
HOST = '172.25.254.250'
USER= 'root'
PASSWD = 'westos'
PORT = 3306
DBNAME = 'User'
用來簡易資料庫連結這一塊
登陸時,只需要匯入該模組相對應的類,來判斷使用者資訊
三 判斷使用者是否登陸的裝飾器
有時候一些網站的資訊,是不對外開放的,只有在登陸狀態下才是可讀的,這時候就要求對其進行限制的設定
import functools def is_login(f): @functools.wraps(f) def wrapper(*args, **kwargs): # run函式程式碼裡面, 如果登陸, session加入user, passwd兩個key值; # run函式程式碼裡面, 如果登出, session刪除user, passwd兩個key值; # 如果沒有登陸成功, 則跳轉到登陸介面 if 'user' not in session: return redirect('/login/') # 如果使用者是登陸狀態, 則訪問哪個路由, 就執行哪個路由對應的檢視函式; return f(*args, **kwargs) return wrapper
判斷使用者是否登陸
import random
import functools
import psutil
from flask import Flask, request, render_template, redirect, url_for, abort,session
import os
from datetime import datetime
fla=Flask(__name__)
fla.config['SECRET_KEY']=random._urandom(24)
userinfo=[
{'user':'root',
'passwd':'westos'
},
]
#系統監控裝飾器
def is_login(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
# run函式程式碼裡面, 如果登陸, session加入user, passwd兩個key值;
# run函式程式碼裡面, 如果登出, session刪除user, passwd兩個key值;
# 如果沒有登陸成功, 則跳轉到登陸介面
if 'user' not in session:
return redirect('/login/')
# 如果使用者是登陸狀態, 則訪問哪個路由, 就執行哪個路由對應的檢視函式;
return f(*args, **kwargs)
return wrapper
@fla.route('/')
def index():
return render_template('index.html')
@fla.route('/login/',methods=['GET','POST'])
def login():
if request.method == 'POST':
user=request.form['user']
passwd=request.form['passwd']
for dbuser in userinfo:
if user == dbuser['user'] and passwd == dbuser['passwd']:
session['user']=user
session['passwd']=passwd
return redirect(url_for('index'))
else:
return render_template('login.html',message='使用者名稱或者密碼錯誤')
else:
return render_template('login.html')
@fla.route('/signup/',methods=['GET','POST'])
def signup():
if request.method == 'POST':
user=request.form['user']
passwd=request.form['passwd']
for dbuser in userinfo:
if user == dbuser['user']:
message = '使用者已經存在'
return render_template('signup.html',message=message)
else:
userinfo.append(dict(user=user,passwd=passwd))
return redirect(url_for('login'))
else:
return render_template('signup.html')
@fla.route('/logout/')
def logout():
session.pop('user',None)
session.pop('passwd',None)
return redirect(url_for('index'))
@fla.route('/sysinfo/')
@is_login
def sysinfo():
info=os.uname()
boot_time=psutil.boot_time()
boot_time=datetime.fromtimestamp(boot_time)
now_time=datetime.now()
delta_time=now_time-boot_time
delta_time=str(delta_time).split('.')[0]
return render_template('sysinfo.html',
hostname=info.nodename,
sysname=info.sysname,
release=info.release,
machine=info.machine,
version=info.version,
now_time=now_time,
boot_time=boot_time,
delta_time=delta_time
)
@fla.errorhandler(404)
def not_found(e):
return render_template('404.html')
@fla.route('/user/<int:user_id>')
def user(user_id):
if 0<int(user_id)<=100:
return '歡迎訪問: %s' %(user_id)
else:
abort(404)
fla.run()
執行結果:
檢視系統資訊時,調轉到登陸頁面,登陸後,放了檢視
四 上傳檔案
將檔案上傳至指定的資料夾中
import os
from flask import Flask, request, render_template
import uuid
app = Flask(__name__)
@app.route('/upload/', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
# 獲取到使用者上傳的檔案物件
f = request.files['faceImg']
print(f.filename)
# 獲取當前專案所在目錄位置;
basepath = os.path.dirname(__file__)
print(basepath)
# 拼接路徑, 儲存檔案到static/face/xxxx
filename = os.path.join(basepath, 'static/face', f.filename)
# 儲存檔案
f.save(filename)
return render_template('demo/upload.html', message = "上傳成功")
else:
return render_template('demo/upload.html')
app.run()
仍舊是剛才那個頁面
import random
import functools
import psutil
from flask import Flask, request, render_template, redirect, url_for, abort,session
import os
from datetime import datetime
fla=Flask(__name__)
fla.config['SECRET_KEY']=random._urandom(24)
userinfo=[
{'user':'root',
'passwd':'westos'
},
]
#系統監控裝飾器
def is_login(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
# run函式程式碼裡面, 如果登陸, session加入user, passwd兩個key值;
# run函式程式碼裡面, 如果登出, session刪除user, passwd兩個key值;
# 如果沒有登陸成功, 則跳轉到登陸介面
if 'user' not in session:
return redirect('/login/')
# 如果使用者是登陸狀態, 則訪問哪個路由, 就執行哪個路由對應的檢視函式;
return f(*args, **kwargs)
return wrapper
@fla.route('/')
def index():
return render_template('index.html')
@fla.route('/login/',methods=['GET','POST'])
def login():
if request.method == 'POST':
user=request.form['user']
passwd=request.form['passwd']
for dbuser in userinfo:
if user == dbuser['user'] and passwd == dbuser['passwd']:
session['user']=user
session['passwd']=passwd
return redirect(url_for('index'))
else:
return render_template('login.html',message='使用者名稱或者密碼錯誤')
else:
return render_template('login.html')
@fla.route('/signup/',methods=['GET','POST'])
def signup():
if request.method == 'POST':
user=request.form['user']
passwd=request.form['passwd']
for dbuser in userinfo:
if user == dbuser['user']:
message = '使用者已經存在'
return render_template('signup.html',message=message)
else:
userinfo.append(dict(user=user,passwd=passwd))
return redirect(url_for('login'))
else:
return render_template('signup.html')
@fla.route('/logout/')
def logout():
session.pop('user',None)
session.pop('passwd',None)
return redirect(url_for('index'))
@fla.route('/sysinfo/')
@is_login
def sysinfo():
info=os.uname()
boot_time=psutil.boot_time()
boot_time=datetime.fromtimestamp(boot_time)
now_time=datetime.now()
delta_time=now_time-boot_time
delta_time=str(delta_time).split('.')[0]
return render_template('sysinfo.html',
hostname=info.nodename,
sysname=info.sysname,
release=info.release,
machine=info.machine,
version=info.version,
now_time=now_time,
boot_time=boot_time,
delta_time=delta_time
)
@fla.route('/upload/', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
# 獲取到使用者上傳的檔案物件
f = request.files['faceImg']
print(f.filename)
# 獲取當前專案所在目錄位置;
basepath = os.path.dirname(__file__)
print(basepath)
# 拼接路徑, 儲存檔案到static/face/xxxx
filename = os.path.join(basepath, 'static/face', f.filename)
# 儲存檔案
f.save(filename)
return render_template('upload.html', message = "上傳成功")
else:
return render_template('upload.html')
@fla.errorhandler(404)
def not_found(e):
return render_template('404.html')
@fla.route('/user/<int:user_id>')
def user(user_id):
if 0<int(user_id)<=100:
return '歡迎訪問: %s' %(user_id)
else:
abort(404)
fla.run()
執行結果:
五 表單處理
注意下列幾個問題:
- 為什麼使用Flask-WTF
request物件公開了所有客戶端傳送的請求資訊。特別是request.form可以訪問POST請求提交的表單資料。
儘管Flask的request物件提供的支援足以處理web表單,但依然有許多工會變得單調且重複。
表單的HTML程式碼生成和驗證提交的表單資料就是兩個很好的例子。
優勢:
Flask-WTF擴充套件使得處理web表單能獲得更愉快的體驗。該擴充套件是一個封裝了與框架無關的WTForms包的Flask整合。
- 什麼是表單處理
在網頁中,為了和使用者進行資訊互動總是不得不出現一些表單。
flask設計了WTForm表單庫來使flask可以更加簡便地管理操作表單資料。
WTForm中最重要的幾個概念如下:
1). Form類,開發者自定義的表單必須繼承自Form類或者其子類。
Form類最主要的功能是通過其所包含的Field類提供對錶單內資料的快捷訪問方式。
2). 各種Field類,即欄位。一般而言每個Field類都對應一個input的HTML標籤。
比如WTForm自帶的一些Field類比如BooleanField就對應< input type=“checkbox”>,SubmitField就對應等等。
3). Validator類。這個類用於驗證使用者輸入的資料的合法性。
比如Length驗證器可以用於驗證輸入資料的長度,FileAllowed驗證上傳檔案的型別等等。
4).另外,flask為了防範csfr(cross-site request forgery)攻擊,預設在使用flask-wtf之前要求app一定要設定過secret_key。
最簡單地可以通過app.config[‘SECRET_KEY’] = 'xxxx’來配置。
- 常見的Field類
PasswordField 密碼欄位,自動將輸入轉化為小黑點
DateField 文字欄位,格式要求為datetime.date一樣
IntergerField 文字欄位,格式要求是整數
DecimalField 文字欄位,格式要求和decimal.Decimal一樣
FloatField 文字欄位,值是浮點數
BooleanField 複選框,值為True或者False
RadioField 一組單選框
SelectField 下拉列表,需要注意一下的是choices引數確定了下拉選項,但是和HTML中的 標籤一樣,其是一個tuple組成的列表,可以認為每個tuple的第一項是選項的真正的值,而第二項是alias。
MultipleSelectField 可選多個值的下拉列表
- Validator是驗證函式
Validator是驗證函式,把一個欄位繫結某個驗證函式之後,flask會在接收表單中的資料之前對資料做一個驗證,如果驗證成功才會接收資料。驗證函式Validator如下,具體的validator可能需要的引數不太一樣,這裡只給出一些常用的,更多詳細的用法可以參見wtforms/validators.py檔案的原始碼,參看每一個validator類需要哪些引數:基本上每一個validator都有message引數,指出當輸入資料不符合validator要求時顯示什麼資訊。
Email 驗證電子郵件地址的合法性,要求正則模式是^[email protected]([ ^ [email protected]][ ^ @]+)$
EqualTo 比較兩個欄位的值,通常用於輸入兩次密碼等場景,可寫引數
fieldname,不過注意其是一個字串變數,指向同表單中的另一個欄位的字
段名
IPAddress 驗證IPv4地址,引數預設ipv4=True,ipv6=False。如果想要驗證ipv6可以設定這兩個引數反過來。
Length 驗證輸入的字串的長度,可以有min,max兩個引數指出要設定的長度下限和上限,注意引數型別是字串,不是INT!!
NumberRange 驗證輸入數字是否在範圍內,可以有min和max兩個引數指出數字上限下限,注意引數型別是字串,不是INT!!然後在這個validator的message引數裡可以設定%(min)s和%(max)s兩個格式化部分,來告訴前端這個範圍到底是多少。其他validator也有這種類似的小技巧,可以參看原始碼。
Optional 無輸入值時跳過同欄位的其他驗證函式
Required 必填欄位
Regexp 用正則表示式驗證值,引數regex=‘正則模式’
URL 驗證URL,要求正則模式是 ^ [a-z]+://(?P< host>[ ^ /:]+)(?P< port>:[0-9]+)?(?P< path> \ /.*)?$
AnyOf 確保值在可選值列表中。引數是values(一個可選值的列表)。特別提下,和SelectField進行配合使用時,不知道為什麼SelectField的choices中項的值不能是數字。。否則AnyOf的values引數中即使有相關數字也無法識別出當前選項是合法選項。我懷疑NoneOf可能也是一樣的套路。
NoneOf 確保值不在可選值列表中
來個簡單的例子:
主函式:
import random
from flask import Flask, redirect, render_template
from forms import LoginForm
from flask_bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app)
app.config['SECRET_KEY'] = random._urandom(24)
@app.route('/success/')
def success():
return "success"
@app.route('/login/', methods=('GET', 'POST'))
def submit():
# 例項化表單物件;
form = LoginForm()
if form.validate_on_submit():
print(form.data)
return redirect('/success/')
return render_template('demo/login.html', form=form)
app.run()
forms.py檔案:用來設定規則
from flask_wtf import FlaskForm
from wtforms import StringField,PasswordField
from wtforms.validators import DataRequired, Length
class LoginForm(FlaskForm):
name=StringField(
label='使用者名稱/郵箱/密碼',
validators=[
DataRequired('請輸入使用者名稱'),
Length(3,20)
]
)
passwd = PasswordField(
label='密碼',
validators=[
DataRequired('請輸入密碼'),
Length(3,20)
]
)
html頁面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="/">
{{form.hidden_tag()}}
{{form.name.label}}{{form.name}}
{{form.passwd.label}}{{form.passwd}}
<input type="submit" value="Go">
</form>
</body>
</html>
執行結果: