FLASK框架
簡介:
flask是一個非常小的python web框架 只提供了一個強勁的核心 其它都是通過第三方擴展庫來實現(xiàn)
安裝:
sudo pip install flask
虛擬環(huán)境
安裝虛擬環(huán)境 pip install virtualenv
創(chuàng)建自己的虛擬環(huán)境 virtualenv --no-site-packages 虛擬環(huán)境的名稱
啟動虛擬環(huán)境 source venv/bin/activate
退出虛擬環(huán)境 deactivate (退出以后 進入到的是全局的·python環(huán)境)
虛擬環(huán)境遷移
pip freeze
pip list
pip freeze > requirment.txt
pip freeze
pip list
pip install -r requirment.txt
解釋:虛擬環(huán)境 就是相當于自己新裝了一個python的解釋器 不受全局解釋器 中的包的影響(獨立) 如果在虛擬環(huán)境中 安裝了flask,在退出虛擬環(huán)境以后 使用不了 flask的 因為你的全局的python解釋器 中沒有安裝flask
一、完整的啟動
from flask import Flask #導入Flask
app = Flask(__name__) #__name__是必傳參數(shù) flask類的實例化
#路由地址
#127.0.0.1:5000/
@app.route('/') #裝飾器路由
def index(): #視圖函數(shù)名稱
return 'hello flask'
#只在當前主文件中啟動 flask
if __name__ == '__main__':
app.run() #運行flask
二、app.run() 啟動參數(shù)
| 參數(shù) | 參數(shù)說明 |
|---|---|
| debug | 調試模式 默認為False 需要啟動改為True |
| port | 端口號 默認5000 |
| host | 主機地址 默認127.0.0.1 ~ 127.255.255.254 |
| threaded | 線程 默認False True開啟多線程 |
實例:
#只在當前主文件中啟動 flask
if __name__ == '__main__':
app.run(host='127.0.0.1',port=5001,debug=True,threaded=True) #運行flask
app.run(debug=True) #開啟調試模式
三、視圖函數(shù)
(1) 無參路由
@app.route('/')
def index():
return '首頁'
(2) 帶參路由
#訪問地址 127.0.0.1:5000/welcome/zhangsan/
@app.route('/welcome/<name>/')
def welcome(name):
# return '必須有響應'
return '歡迎'+name
(3) 傳遞多個參數(shù)
#帶多個參數(shù)
#http://127.0.0.1:5000/args/zhansan/18/
# @app.route('/args/<name>/<age>/')
# http://127.0.0.1:5000/args/zhansan_18/
@app.route('/args/<name>_<age>/')
def args(name,age):
return '我叫{} 我今年{}歲了'.format(name,age)
(4) 限制參數(shù)的類型
@app.route('/demo/<int:age>/') #限制傳參類型為int
@app.route('/demo/<float:age>/') #限制傳參類型為float
@app.route('/demo/<string:age>/') #默認就是字符串
@app.route('/demo/<path:age>/') #把參數(shù)age位置的參數(shù)中的/不再認為是路由地址的分隔符,而是參數(shù)的一部分 屬于字符串類型的一部分
#/demo/zhangsan/a/b/c/
@app.route('/demo/<name>/<path:age>/') #把參數(shù)age位置的參數(shù)中的/不再認為是路由地址的分隔符,而是參數(shù)的一部分 屬于字符串類型的一部分
# http://127.0.0.1:5000/demo/zhangsan/a/b/c/
@app.route('/demo/<path:age>/<name>/') #把參數(shù)age位置的參數(shù)中的/不再認為是路由地址的分隔符,而是參數(shù)的一部分 屬于字符串類型的一部分
def demo(age,name):
print(age,name)
# print(type(age))
return '我是測試的視圖'
注意:
- 每個路由地址結尾的/ 建議都加上 如果路由地址存在結尾的/ 瀏覽器會自動加上 如果沒有結尾的 / 請求時 存在/ 則請求失敗
- 傳參的格式 <參數(shù)名稱>
- 傳遞多個參數(shù) 使用/分隔符 或者_進行多個參數(shù)的拼接
- 參數(shù)傳遞進來都為字符串類型(默認)
- 限制參數(shù)類型 int/float/string/path
四、重定向
從一個視圖跳轉到另外一個視圖的操作
from flask import redirect,url_for
(1) redirect 通過路由地址跳轉
實例
@app.route('/')
def index():
return 'index.html'
@app.route('/args/<name>/<age>/')
def args(name,age):
return name+age
@app.route('/redirect/')
def myRedirect():
return redirect('/') #重定向到首頁
return redirect(/args/zhansgan/18/)
(2) url_for 通過視圖函數(shù)名 反向解析出路由地址
實例
@app.route('/')
def index():
return 'index.html'
@app.route('/args/<name>/<age>/')
def args(name,age):
return name+age
@app.route('/redirect/')
def myRedirect():
print(url_for('index')) # /
print(url_for('indexx')) # 報錯 當前視圖函數(shù)不存在
print(url_for('args',name='zhangsan',age=18) #/args/zhangsan/18/
return redirect(url_for('index')) #重定向到首頁
return redirect(url_for('args'name='zhangsan',age=18))
注意:
當url_for 構造不存在的視圖函數(shù)時 則報錯
當url_for 構造需要傳參的路由地址的時候 不存參則報錯
五、abort(http標準狀態(tài)碼) 終止
from flask import abort
實例
@app.route('/')
def index():
print('abort上方')
# abort(404)
abort(500)
print('abort下方')
# return 'index.html'
參數(shù)e接受錯誤的信息
#捕獲404狀態(tài)碼的錯誤信息
@app.errorhandler(404)
def page_not_found(e):
print(e)
# return 'page_not_found'
return e
#捕獲500狀態(tài)碼的錯誤信息
@app.errorhandler(500)
def server_error(e):
return 'server_error'
注意:
abort上面的代碼正常執(zhí)行 下面的代碼不再執(zhí)行 和raise一樣
六、響應 response
概述: 我們響應 可以給瀏覽器響應文字內容,html頁面的代碼 和 狀態(tài)碼 創(chuàng)建cookie
(1) 使用return 文字內容
@app.route('/')
def index():
return 'index.html'
(2) 響應內容并指定響應的狀態(tài)碼
@app.route('/')
def index():
return 'index.html',404
(3) 通過make_response 構造響應
from flask imoprt make_response
@app.route('/make_response/')
def makeResponse():
# res = make_response('make_response響應的內容')
res = make_response('make_response響應的內容',404)
return res
七、cookie 會話控制
協(xié)議:http
http:超文本傳輸協(xié)議 無狀態(tài)協(xié)議
(1) 設置cookie 不設置過期時間
注意: 默認存活時間為 瀏覽會話結束(關閉瀏覽器)
主體結構
Response.set_cookie(
? key,
? value,
? max_age=None #設置過期時間 單位為秒數(shù)
? expires =None, #設置過期時間 為時間戳的秒數(shù)
? path = '/' #設置當前cookie存儲為全局
)
實例
#cookie的操作
@app.route('/set_cookie/')
def set_cookie():
res = make_response('設置cookie')
res.set_cookie('name','zhangsan')
return res
(2) 獲取cookie
@app.route('/get_cookie/')
def get_cookie():
# print(request.cookies['name']) #獲取請求過來的cookie
# print(request.cookies.get('name')) #獲取請求過來的cookie
return 'cookie中name的值為{}'.format(request.cookies.get('name','沒有值'))
(3) 刪除cookie
@app.route('/del_cookie/')
def del_cookie():
res = make_response('刪除name的cookie')
res.delete_cookie('name')
return res
(4) 設置cookie 并設置過期時間
#cookie的操作
@app.route('/set_cookie/')
def set_cookie():
res = make_response('設置cookie')
# res.set_cookie('name','zhangsan') #不舍值過期時間
# res.set_cookie('name','zhangsan',max_age=60) #max_age 存活的秒數(shù)
lift_time = time.time()+40 #設置40秒以后的時間戳
res.set_cookie('name','zhangsan',expires=lift_time) #expires 值為存活的時間戳
return res
八、session
概述:服務器要識別 不同的用戶的請求 根據(jù)cookie攜帶著的唯一session_id 值 來進行區(qū)分不同用戶的請求
所以session基于cookie 如果cookie被禁用了 session也不能使用
session加密需要加密種子 secret_key 進行加密
(1) 設置session
注意: 不設置過期時間 默認為瀏覽會話結束
@app.route('/set_session/')
def index():
session['username'] = '張三'
return '設置set_session'
(2) 獲取session
@app.route('/get_session/')
def get_session():
return '獲取session 值為{}'.format(session.get('username'))
(3) 刪除session
@app.route('/del_session/')
def del_session():
session.pop('username') #刪除key為username的數(shù)據(jù)
# session.clear() #刪除全部
return '刪除session'
(4) 設置session及過期時間
@app.route('/set_session/')
def index():
session.permanent = True #持久化存儲
app.permanent_session_lifetime = timedelta(minutes=5) #設置存活時間為5分鐘
session['username'] = '張三'
return '設置set_session'
day2
一、request對象
瀏覽器帶著用戶的請求 經由flask框架創(chuàng)建出 request對象 request對象 包含當前用戶的所有的請求信息
導入
from flask import request
request對象屬性
url 完整的url請求
base_url 去掉get傳參的url
host_url 只有主機地址和端口號
path 裝飾中的路由地址
method 請求方法
-
args 獲取get傳參
request.args.get(key) 只能獲取唯一key的值 如果key存在重復 則獲取不到其它值
request.args.getlist(key) 獲取key以列表形式返回(拿到所有的value)
form 獲取表單post傳遞過來的數(shù)據(jù)
files 獲取文件上傳的數(shù)據(jù)
headers 獲取請求頭信息
cookies 獲取所有的cookie
json 獲取json數(shù)據(jù)
二、flask-script擴展
概念: 就是一個flask終端運行的解析器 因為在項目完成以后 就不應該有任何的改動 所有通過終端 添加不同的參數(shù) 來進行不同的啟動項
安裝
sudo pip3 install flask-script
使用
from flask_script import Manager
app = Flask(__name__)
manager= Manager(app)
...
if __name__ == '__main__':
manager.run()
啟動命令
python3 文件名稱.py runserver -d -r -h127.0.0.1 -p5000 --threaded
python3 manage.py runserevr -d -r #開啟debug和自動加載
三、藍本 blueprint
概念: 將視圖拆分到不同的文件中 藍本就是解決這個問題的
導入
from flask import Blueprint
user.py
from flask import Blueprint,request #導入藍本
user = Blueprint('user',__name__)
@user.route('/login/')
def login():
return '登陸'
manage.py
from user import user #導入user藍本對象
#注冊藍本
app.register_blueprint(user) #127.0.0.1:5000/login/
#設置當前藍本的前綴
# app.register_blueprint(user,url_prefix='/user') #127.0.0.1:5000/user/login/
藍本中的重定向
index->login
@app.route('/')
def index():
# return redirect(url_for('login')) #錯誤寫法 報錯
return redirect(url_for('user.login')) #user藍本中的login視圖函數(shù)
四、請求鉤子函數(shù)
類Django框架的中間件
在主程序中使用
| 鉤子函數(shù) | 函數(shù)說明 |
|---|---|
| before_first_request | 第一次請求之前 |
| before_request | 每次請求之前 |
| after_request | 每次請求之后 |
| teardown_request | 每次請求之后 即使有異常出現(xiàn) |
實例
#第一次請求之前
@app.before_first_request
def before_first_request():
print('before_first_request')
#每次請求之前
@app.before_request
def before_request():
if request.path == '/form/' and request.method == 'GET':
abort(404)
print('before_request')
#每次請求之后
@app.after_request
def after_request(res):
print('after_request')
return res
#每次請求之后
@app.teardown_request
def teardown_request(res):
print('teardown_request')
return res
在藍本中使用
| 鉤子函數(shù) | 函數(shù)說明 |
|---|---|
| before_app_first_request | 第一次請求之前 |
| before_app_request | 每次請求之前 |
| after_app_request | 每次請求之后 |
| teardown_app_request | 每次請求之后 即使有異常出現(xiàn) |
實例
#在藍本中
#第一次請求之前
@user.before_app_first_request
def before_first_request():
print('before_first_request')
#每次請求之前
@user.before_app_request
def before_request():
if request.path == '/form/' and request.method == 'GET':
abort(500)
print('before_request')
#每次請求之后
@user.after_app_request
def after_request(res):
print('after_request')
return res
#每次請求之后
@user.teardown_app_request
def teardown_request(res):
print('teardown_request')
return res
模板引擎
模板引擎: 就是一定規(guī)則 書寫 和 替換 最終展示成完整的html文件 響應給用戶
模板引擎: jinja2
目錄結構
project/
templates/ 模板目錄
manage.py
(1) flask中渲染模板的函數(shù)
導入
from flask import render_template,render_template_string
render_template() #將模板渲染后響應給用戶
@app.route('/')
def index():
# return render_template('index.html')
print(render_template('index.html')) #render_templates將模板代碼渲染以后 響應給用戶
return 'index.html'
render_template_string() #渲染字符串 響應給用戶
@app.route('/')
def index():
return render_template_string('世界上最牛的字是哪一個?<span style="font-size:16px;color:red;">昊</span>')
(2) 模板中 分為倆大類
-
變量
{{ 變量名稱 }}
注意:
? 如果在模板中存在變量 且視圖函數(shù)沒有傳遞 則插入空白字符(什么都沒有) 不報錯
實例
@app.route('/') def index(): return render_template('index.html',title='首頁') <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <h1>首頁</h1> </body> </html>
-
標簽
{% 標簽名稱 %}
(3) 過濾器
根據(jù)管道符 | 對傳遞的值進行過濾
-
abs 絕對值
val|abs -
default 默認值
默認情況 只有給定的變量不存在則執(zhí)行 默認值
{{ var|default('默認值') }}
{{ False|default('默認值') }} 不走
{{ 不存在的變量|default('默認值') }} 執(zhí)行默認值
設置default執(zhí)行flase的默認值
{{ var|default('默認值',boolean=True) }}
-
first 返回序列中的第一個
-
last 返回序列中的最后一個值
-
format 格式化字符串的輸出
<li>{{ "我叫%s 我今年%d歲了 存款為%.2f元"|format('張三',20,1234) }}</li> -
length 長度
-
join 拼接字符串
-
safe 不轉義代碼
-
int 轉換為整形
-
float 轉換為浮點形
-
list 轉換為列表
-
lower 轉換小寫
-
upper 轉換為大寫
-
replace 替換
-
trim 去除倆測空白字符
-
striptags 去除html標簽
使用的安裝
sudo pip3 install 模塊名稱
- flask
- flask-script
- flask-moment
- flask-cache
- flask-uploads
- flask-wtf
- flask-bootstrap
- flask-sqlalchemy
- pillow
- pymysql
- flask-mail
- flask-migrate
(4) 標簽
格式{% tag %}
if 標簽
if...elif..else
{% if 90<=data.grade<=100 %}
<p>優(yōu)</p>
{% elif 80<=data.grade %}
<p>良</p>
{% elif 70<=data.grade %}
<p>中</p>
{% elif 60<=data.grade %}
<p>合格</p>
{% else %}
<p>不及格</p>
{% endif %}
for 標簽
{% for i in ... %}
? 循環(huán)體
{% else %}
? ...
{% endfor %}
<ol>
{# {% for i in range(10) %}#}
{% for i in xx %}
{% for k,v in data.items() %} #迭代字典
<li>{{ i }}</li>
{% else %}
<li>你是否能看到我</li>
{% endfor %}
</ol>
注意:
當?shù)淖兞?不存在 則執(zhí)行else
獲取迭代的狀態(tài)
| 變量 | 描述 |
|---|---|
| loop.index | 當前迭代的索引 從1開始 |
| loop.index0 | 當前迭代的索引 從0開始 |
| loop.first | 是否為第一次迭代 |
| loop.last | 是否為最后一次迭代 |
| loop.length | 迭代的長度 |
(5) 文件的包含 include
格式
{% include "路徑/文件.html" %}
把文件引入到當前的位置 實現(xiàn)模板代碼的復用
實例
header.html
<header>
頭部
</header>
footer.html
<footer>
尾部
</footer>
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{#{% include 'index.html' %}#}
{% include 'common/header.html' %}
<div>content內容部分</div>
{% include 'common/footer.html' %}
</body>
</html>
注意:
include 會將導入的代碼 相當于粘貼到了 你導入的位置上 導入文件的所有代碼都會在導入的位置顯示出來
(6) 宏 macro
概述
宏相當于 python中的函數(shù) 需要定義 調用 傳參
主體結構
{% macro 宏名稱([參數(shù)...]) %}
內容
{% endmacro %}
調用
{{ 宏的名稱([參數(shù)...]) }}
導入
在common下創(chuàng)建pubulicform.html文件
在form.html中導入
實例
from ... import ...
{% from 'common/publicform.html' import form %}
{{ form('用戶名','text','username') }}
{{ form('密碼','password','userpass') }}
{{ form('','submit','','提交') }}
import ...
{% import 'common/publicform.html' as publicform %}
{{ publicform.form('用戶名','text','username') }}
{{ publicform.form('密碼','password','userpass') }}
{{ publicform.form('','submit','','提交') }}
注意
- 宏的調用只能在宏的下方來調用
- 宏如果有形參 且沒有默認值 則可以不傳實參
- 宏如果沒有形參 則不可以傳實參
- 宏的形參默認值 和python一樣
- 宏的默認值 遵循默認值規(guī)則 有默認值的放在右側 沒有默認值的 放在左側
- 關鍵字的參數(shù)使用 同python函數(shù)一樣
day3
一、模板的繼承 extends
概念: flask中的模板繼承 可以通過繼承 將很多重復的元素 抽離出來 放在父模板中 通過模板中 的extends,block 實現(xiàn)父模板繼承和替換的使用
語法
{% extends '父模板的名稱.html' %}
{% block 替換的名稱 %} 要替換的內容部分 {% endblock %}
自定義一個base.html模板
<!DOCTYPE html>
<html lang="en">
{% block header %}
<head>
{% block meta %}
<meta charset="UTF-8">
{% endblock %}
<title>{% block title %}base{% endblock %}</title>
<style>
{% block headercss %}
div{
font-size: 18px;
color: aqua;
}
{% endblock %}
</style>
{% block linkscript %}
{% endblock %}
</head>
{% endblock %}
<body>
<header>
頭部
</header>
<div id="content">
{% block content %}
主體內容部分
{% endblock %}
</div>
<footer>
底部
</footer>
</body>
</html>
子模板 index.html
{% extends 'common/base.html' %}
{% block title %}
首頁
{% endblock %}
{% block headercss %}
{{ super() }}
ul,li{
list-style:none;
margin:0;
padding:0;
}
li{
width:100px;
height:100px;
border-radius:50px;
background-color:yellow;
}
#content{
color:red;
}
{% endblock %}
{% block content %}
<h1>首頁內容</h1>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
{% endblock %}
{# 以下代碼不會被加載 #}
{#{% block newblock %}#}
{# <h1>首頁內容</h1>#}
{#{% endblock %}#}
{#sdasjdjsaldjaslkdalksjlksajdlasjdalskdjalkdsaskl#}
注意:
不可以在base模板block 以外的位置 添加任何的代碼 因為都不會被加載
{{ super() }} 將base.html模板中替換的代碼在調用(復用)
二、flask-bootstrap flask的bootstrap第三方擴展庫
安裝:
sudo pip3 install flask-bootstrap
使用
自定義base.html
{% extends 'bootstrap/base.html' %}
{% block title %}
boot-base 更改自己的base
{% endblock %}
{% block navbar %}
<nav class="navbar navbar-inverse" style="border-radius:0px;">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#"><span class="glyphicon glyphicon-eye-open"
aria-hidden="true"></span></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">首頁<span class="sr-only">(current)</span></a></li>
<li><a href="#">發(fā)表帖子</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<li><a href="#">登錄</a></li>
<li><a href="#">注冊</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">個人中心 <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{% endblock %}
{% block content %}
<div class="container">
{% block pagecontent %}
<p>少小離家老大回</p>
<p>安能辯我是雌雄</p>
{% endblock %}
</div>
{% endblock %}
三、錯誤頁面定制
manage.py
#錯誤頁面定制
@app.errorhandler(404)
def page_not_found(e):
return render_template('error.html',code=404,e=e,info='您訪問的頁面被外星人抓走了')
@app.errorhandler(500)
def page_not_found(e):
return render_template('error.html',code=500,e=e,info='我們內部有些事情需要解決 請稍后訪問')
error.html
{% extends 'common/boot-base.html' %}
{% block title %}
{{ code }}頁面
{% endblock %}
{% block pagecontent %}
<h3>{{ info }}</h3>
<div class="alert alert-warning" role="alert"><h4>{{ code }}</h4>{{ e }}</div>
{% endblock %}
四、靜態(tài)資源
創(chuàng)建static靜態(tài)資源目錄
目錄結構
project/
templates/
common/
main/
index.html
....
static/
img/
css/
js/
upload/
manage.py
實例
{% block pagecontent %}
<h3>{{ info }}</h3>
<div class="alert alert-warning" role="alert"><h4>{{ code }}</h4>{{ e }}</div>
<img src="{{ url_for('static',filename='img/timg.jpg',_external=True) }}" alt="">
{% endblock %}
五、視圖傳遞多個參數(shù)
(1) 原始傳參
@app.route('/')
def index():
return render_template('index.html',arg1=v1,arg2=v2...)
(2) 使用字典
@app.route('/')
def index():
return render_template('index.html',data={'arg':'v1','arg2':'v2'...})
(3) 使用**
@app.route('/')
def index():
data={'arg':'v1','arg2':'v2'...}
return render_template('index.html',**data)
(4) locals()
以字典的形式獲取局部變量
@app.route('/')
def index():
return render_template('index.html',**locals())變量
表單
六、flask原生表單
form1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>原生表單</title>
</head>
<body>
<form action="{{ url_for('check_form',_external=True) }}" method="post">
<p>用戶名 <input type="text" name="username"></p>
<p>密碼 <input type="password" name="userpass"></p>
<p><input type="submit" value="submit"></p>
</form>
</body>
</html>
manage.py
#跳轉到登錄的表單頁
@app.route('/login/')
def login():
return render_template('form1.html')
#表單提交過來處理的視圖函數(shù)
@app.route('/check_form/',methods=['POST'])
def check_form():
print(request.form.get('username'))
print(request.form.get('userpass'))
return '提交過來了'
倆個合并為同一個
#表單提交過來處理的視圖函數(shù)
@app.route('/login/',methods=['GET','POST'])
def check_form():
if request.method == 'POST':
print(request.form.get('username'))
print(request.form.get('userpass'))
return render_template('form1.html')
七、flask-wtf
概述: 是一個用于處理flask表單的擴展庫 提供了表單校驗 csrf等功能
安裝
sudo pip3 install flask-wtf
csrf_token 的生成 需要依賴flask的secret_key
python中常量:一般用于配置文件的設置 變量的大寫 在代碼運行過程中 不能被修改和銷毀的量 稱之為常量(python中沒有)
(1) 字段屬性
| 字段類型 | 字段說明 |
|---|---|
| StringField | 普通文本字段 |
| SubmitField | 提交按鈕 |
| PasswordField | 密碼框 |
| HiddenField | 隱藏域 |
| TextAreaField | 多行文本域 |
| DateField | 日期 |
| DateTimeFIeld | 時間和日期 |
| IntegerField | 整數(shù) |
| FloatField | 浮點數(shù) |
| BooleanField | 布爾字段 |
| RadioFIeld | 單選框 |
| SelectField | 下拉 |
| FileField | 文件上傳 |
(2) 驗證器
| 驗證器 | 驗證器說明 |
|---|---|
| DataRequired | 必填 |
| 郵箱 | |
| IPAddress | ip地址 |
| Length | 長度 |
| NumberRang | 值的范圍 |
| EqualTo | 驗證倆個字段的值是否相同 |
| URL | 驗證url地址 |
| Regexp | 正則驗證 |
實例
form2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{# 添加csrf——token驗證 #}
<p>{{ form.csrf_token }}</p>
<p>{{ form.username.label() }}</p>
<p>{{ form.username() }}
{# 顯示錯誤信息的判斷 #}
{% if form.username.errors %}
{{ form.username.errors.0 }}
{% endif %}
</p>
<p>{{ form.userpass.label() }}</p>
<p>{{ form.userpass() }}
{# 顯示錯誤信息的判斷 #}
{% if form.userpass.errors %}
{{ form.userpass.errors.0 }}
{% endif %}
</p>
<p>{{ form.submit() }}</p>
</form>
</body>
</html>
manage.py
from flask import Flask,render_template,request
from flask_script import Manager
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm # 導入表單類的基類
from wtforms import StringField,PasswordField,SubmitField #導入字段類型
from wtforms.validators import DataRequired,Length #導入驗證器
app = Flask(__name__)
# app.secret_key = 'abcd'
app.config['SECRET_KEY'] = 'abcd'
app.config['BOOTSTRAP_SERVE_LOCAL'] = True #加載本地樣式
bootstrap = Bootstrap(app)
manager = Manager(app)
#自定義表單類
class Login(FlaskForm):
#username 為 name=username 用戶名label標簽和內容 validators驗證器
username = StringField('用戶名',validators=[DataRequired(message='用戶名不能為空')])
userpass = PasswordField('密碼',validators=[DataRequired(message='密碼不能為空'),Length(min=6,max=12,message='密碼長度為6~12位')])
submit = SubmitField('登錄')
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login/',methods=['GET','POST'])
def login():
form = Login() #實例化自定義表單類
if form.validate_on_submit(): #如果csrf_token和數(shù)據(jù)驗證都通過則為真
return '數(shù)據(jù)提交過來了'
return render_template('form2.html',form=form)
if __name__ == '__main__':
manager.run()
day5 MODEL
flask作為一款MVT框架 也有自己的ORM flask-sqlalchemy
安裝: sudo pip3 install flask-sqlalchemy
使用ORM原因:
當項目越來越大的時候 如果采用原生SQL的方式 就會有如下問題
- 出現(xiàn)大量的原生SQL,如果條件越多 代碼越長 并且重復使用率低
- 很多的sql語句 都是在業(yè)務邏輯中拼接出來的 如果數(shù)據(jù)庫需要更改 就要去修改這些sql的邏輯 很容易漏掉某些sql的修改
- 寫原生sql容易忽略安全問題
使用ORM好處:
- 易用性 使用ORM做數(shù)據(jù)庫的開發(fā) 可以有效的減少重復SQL的概率 寫出來的代碼 也更加的清晰直觀
- 設計靈活 可以輕松的寫出sql語句
- 可移植性
一、原生sql
(1) 創(chuàng)建一個庫
create database if not exists bj1805 character set utf8;
(2) 創(chuàng)建表
create table if not exists user(
id int primary key auto_increment,
username varchar(20) not null default 'xxx',
sex tinyint default 1
)
(3) 導入 sqlalchemy
from sqlalchemy import create_engine
(4) 配置 URI 鏈接地址
DB_URI = 'mysql+pymysql:// 用戶名:密碼@主機名:端口號/數(shù)據(jù)庫名稱'
實例
from sqlalchemy import create_engine
HOSTNAME = '127.0.0.1'
USERNAME = 'root'
PASSWORD = '123456'
PORT = 3306
DATABASE = 'bj1805'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
engine = create_engine(DB_URI)
#鏈接數(shù)據(jù)庫
with engine.connect() as con:
#創(chuàng)建表
con.execute('create table test(username varchar(20))')
con.execute('insert into test values("xxxx")')
二、flask-sqlalchemy的使用
(1) 導入
from flask_sqlalchemy import SQLalchemy
(2) 配置
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/bj1805'
#是否追蹤數(shù)據(jù)發(fā)生的改變 占用額外資源 將值改為True或者Flase進行禁用
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
(3) 實例化
db = SQLalchemy(app)
三、設計模型
字段類型
| 類型名 | python中的類型 | 類型說明 |
|---|---|---|
| Integer | int | 存儲整形 |
| SmallInteger | int | 小整形 |
| BigInteger | int | 長整形 |
| Float | float | 浮點形 |
| String | str | varchar類型 |
| Text | str | text長文本類型 |
| Boolean | bool | tinyint 整形 |
| Date | datetime.date | 日期 |
| Time | datetime.time | 時間 |
| DateTime | datetime.datetime | 日期和時間 |
可選約束條件
| 選項 | 說明 |
|---|---|
| primary_key | 默認False True創(chuàng)建主鍵索引 |
| unique | 唯一索引 |
| index | 常規(guī)索引 |
| nullable | 是否可以為null 默認True |
| default | 默認值 |
實例:
#創(chuàng)建模型
class User(db.Model):
__tablename__ = 'user' #表名默認為類名 給當前模型起表名
#id 字段名稱 db.Column 整形 主鍵索引
id = db.Column(db.Integer,primary_key=True)
username = db.Column(db.String(20),index=True)
age = db.Column(db.Integer)
sex = db.Column(db.Boolean,default=True) #default默認值 并不是修改表結構 而是在插入數(shù)據(jù)的時候 沒有給值 則將默認值插入
info = db.Column(db.String(100))
#添加構造方法
def __init__(self,username='',age=18,sex=True,info='默認值'):
self.username = username
self.age = age
self.sex = sex
self.info = info
創(chuàng)建模型
@app.route('/create_all/')
def create_all():
db.create_all()
return '創(chuàng)建表'
刪除模型
#刪除表 只刪除和模型類重名的表
@app.route('/drop_all/')
def drop_all():
db.drop_all()
return '刪除表'
四、增刪改查
(1) 添加一條數(shù)據(jù) add
#添加數(shù)據(jù)
@app.route('/insert_one/')
def insert_one():
# u = User(username='張三',age=18,info='張三的個人簡介')
try:
u = User('李四',20,False,'李四的個人簡介')
db.session.add(u)
db.session.commit() #提交
except:
db.session.rollback() #回滾
return '添加一條數(shù)據(jù)'
(2) 添加多條數(shù)據(jù) add_all
#添加多條數(shù)據(jù)
@app.route('/add_many/')
def add_many():
u1 = User('王五',30)
u2 = User('趙六',40)
db.session.add_all([u1,u2])
db.session.commit() #提交
return '添加多條數(shù)據(jù)'
(3) 修改
#修改1
@app.route('/update/')
def update():
u = User.query.get(1) #查詢id為1的數(shù)據(jù)對象
# print(u)
u.info = '堅持到無能為力'
db.session.add(u)
db.session.commit()
return '修改'
(4) 刪除 delete
#刪除
@app.route('/delete/')
def delete():
u = User.query.get(5)
db.session.delete(u) #刪除
db.session.commit()
return '刪除'
五、拆分MVT(簡單的拆分)
project/
App/
__init__.py
views/
models/
settings.py #配置文件
exts.py #extensions 加載第三方擴展庫
manage.py
六、設置提交方式
(1) 設置為自動提交
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
不需要在db.session.commit()
(2) 自定義增刪的基礎類
class Base:
#自定義添加一條數(shù)據(jù)的方法
def save(self):
try:
db.session.add(self)
db.session.commit()
except:
db.session.rollback()
#自定義添加多條的方法
@staticmethod
def save_all(*args):
try:
db.session.add_all(args)
db.session.commit()
except:
db.session.rollback()
#自定義刪除方法
def delete(self):
try:
db.session.delete(self)
db.session.commit()
except:
db.session.rollback()
使用
#創(chuàng)建模型
class User(Base,db.Model):
views.py
@view.route('/insert_one/')
def insert_one():
# u = User('JQKA',16,False)
# db.session.add(u)
# db.session.commit()
sexList = [True,False]
username = random.randrange(11111, 99999)
u1 = User(username, random.randint(1, 70), sexList[random.randint(0, 1)], str(username) + '的個人說明')
u1.save() #添加
return '添加數(shù)據(jù)'
@view.route('/insert_many/')
def insert_many():
sexList = [True,False]
# sexList[random.randint(0,2)]
username = random.randrange(11111,99999)
u1 = User(username,random.randint(1,70),sexList[random.randint(0,1)],str(username)+'的個人說明')
username = random.randrange(11111, 99999)
u2 = User(username, random.randint(1, 70), sexList[random.randint(0,1)], str(username) + '的個人說明')
# db.session.add_all([u1,u2])
User.save_all(u1,u2) #添加多條
return '添加多條數(shù)據(jù)'
@view.route('/delete/')
def delete():
User.query.get(12).delete()
return '刪除'
七、數(shù)據(jù)庫操作
查詢集
查詢數(shù)據(jù)的集合
分類
-
原始查詢集
就是通過類名.query得到的就是原始查詢集
-
數(shù)據(jù)查詢集
通過過濾的方法 最終拿到的數(shù)據(jù)就三數(shù)據(jù)查詢集
(1) all() 得到所有數(shù)據(jù)查詢集 返回列表
類名.query.all()
@view.route('/all/')
def all():
data = User.query.all()
return render_template('show.html',data = data)
(2) filter() 過濾 默認沒有條件 返回所有
類名.query.filter([類名.屬性名 比較運算符 值。。。])
@view.route('/filter/')
def filter():
# data = User.query.filter() #返回所有
data = User.query.filter(User.age<60,User.age>10) #查詢 年齡在59-11之間
return render_template('show.html',data=data)
(3) filter_by() 只支持單條件查詢
沒有條件 則返回所有
屬性=值,,,,,,
類名.query.filter_by(username='張三'....)
@view.route('/filter_by/')
def filter_by():
# data = User.query.filter_by() #返回所有
data = User.query.filter_by(sex=True) #查詢性別為True的所有數(shù)據(jù)
return render_template('show.html',data=data)
(4) offset(num) 偏移量
@view.route('/offset/')
def offset():
data = User.query.offset(5) #偏移5條數(shù)據(jù) 從第六條開始取
return render_template('show.html',data=data)
(5) limit(num) 取值
@view.route('/limit/')
def limit():
data = User.query.limit(5) #取出5條數(shù)據(jù)
return render_template('show.html',data=data)
(6) order_by() 排序
默認升序
-類名.屬性名
@view.route('/order_by/')
def order_by():
# data = User.query.order_by(User.age) #按照年齡升序
data = User.query.order_by(-User.age) #按照年齡降序
return render_template('show.html',data=data)
(7) first() 第一條
類名.query.first()
@view.route('/first/')
def first():
# data = User.query.first() #取出第一條數(shù)據(jù)
data = User.query.order_by(-User.age).first() #取出第一條數(shù)據(jù)
print(data)
return 'first'
(8) get(id值) 根據(jù)ID值進行查詢
查詢成功返回 對象
查詢失敗 返回None
@view.route('/get/')
def Get():
data = User.query.get(1)
print(data)
return 'get'
(9) contains() 包含關系
@view.route('/contains/')
def contains():
data = User.query.filter(User.username.contains('9')) #查詢包含數(shù)字9的數(shù)據(jù)
return render_template('show.html',data=data)
(10) like 模糊查詢
@view.route('/like/')
def like():
# data = User.query.filter(User.username.like('%9%')) #包含9的數(shù)據(jù)
# data = User.query.filter(User.username.like('%4')) #4作為結尾
data = User.query.filter(User.username.like('5%')) #5作為開頭
return render_template('show.html',data=data)
(11) startswith 以...開頭 endswith 以...結尾
@view.route('/with/')
def With():
# data = User.query.filter(User.username.startswith('9')) #以9作為開頭
data = User.query.filter(User.username.endswith('4')) #以4作為結尾
return render_template('show.html',data=data)
(12) 比較運算符
-
__gt__大于 -
__lt__小于 -
__ge__大于等于 -
__le__小于等于 >=<=- ==
- !=
><
實例
@view.route('/bj/')
def bj():
#年齡 大于30 and 年齡小于50
data = User.query.filter(User.age.__gt__(30),User.age.__lt__(50))
return render_template('show.html',data=data)
(13) in 和 not in 在...范圍內 不再 ...范圍內
@view.route('/in/')
def In():
#查詢id在 1,2,3,4,12,13,14范圍內的數(shù)據(jù)
# data = User.query.filter(User.id.in_([1,2,3,4,12,13,14]))
# 查詢id不在 1,2,3,4,12,13,14范圍內的數(shù)據(jù)
data = User.query.filter(~User.id.in_([1,2,3,4,12,13,14]))
return render_template('show.html',data=data)
(14) null 空
@view.route('/null/')
def null():
#查詢username為null的數(shù)據(jù)
# data = User.query.filter(User.username.is_(None))
# data = User.query.filter(User.username==None)
#查詢部位null的數(shù)據(jù)
# data = User.query.filter(User.username.isnot(None))
# data = User.query.filter(~User.username.is_(None))
data = User.query.filter(User.username!=None)
(15) and_ 邏輯與查詢
from sqlalchemy import and_
默認為and查詢
@view.route('/and/')
def And():
# data = User.query.filter(User.sex==True,User.id<10)
data = User.query.filter(and_(User.sex==True,User.id<10))
return render_template('show.html',data=data)
(16) or_ 邏輯或
from sqlalchemy import or_
@view.route('/or/')
def Or():
data = User.query.filter(or_(User.sex==True,User.id<10))
return render_template('show.html',data=data)
(17) and和 or的操作
@view.route('/or/')
def Or():
data = User.query.filter(and_(User.username.contains('9')),or_(User.sex==True,User.id<10))
#select * from user where username like '%9%' and (sex=1 or id<10)
return render_template('show.html',data=data)
(17) not_ 邏輯非
from sqlalchemy import not_
@view.route('/not/')
def Not():
#只能一個條件
data = User.query.filter(not_(User.sex==True))
return render_template('show.html',data=data)
(18) count 統(tǒng)計
@view.route('/count/')
def count():
#統(tǒng)計數(shù)據(jù)條數(shù)
data = User.query.count()
print(data)
return 'count'
# return render_template('show.html',data=data)
一、文件遷移
(1) 安裝
sudo pip3 install flask-migrate
sudo pip3 install flask-script
(2) 使用
from flask_script import Manager
from flask_migrate import Migrate,MigrateCommand
migrate = Migrate(app) #實例化遷移對象
manager = Manager(app)
manager.add_command('db',MigrateCommand) #添加遷移命令
...
if __name__ == '__main__':
manager.run()
(3) 生成遷移目錄
python3 manage.py db init
此刻會在項目中生成一個叫migrations的目錄
(4) 生成遷移文件
python3 manage.py db migrate
(5) 執(zhí)行遷移文件(更新數(shù)據(jù)庫)
python3 manage.py db upgrade
三、拆分MVT的
blog/
App/
static/ #靜態(tài)目錄
img/
css/
js/
upload/
templates/ #模板目錄
common/
base.html
models/ #模型目錄
__init__.py
views/ #視圖目錄
__init.py
forms/ #表單目錄
__init__.py
settings.py #配置文件
extensions.py #加載第三方擴展庫
email.py #郵件
__init__.py #App包的初始化
migrations/ #遷移目錄
venv/ #虛擬環(huán)境
manage.py #啟動文件
分頁顯示博客
paginate 分頁類 返回一個分頁對象 pagination
paginate實例化傳參
page 必須參數(shù) 當前頁碼
per_page 每頁顯示數(shù)據(jù)的條數(shù) 默認20
error_out 當查詢出錯的時候 是否報404的錯誤 默認為True
pagination對象
屬性:
items 當頁的所有數(shù)據(jù)
page 當前頁碼
pages 總頁碼是
total 總數(shù)據(jù)條數(shù)
per_page 每頁多少條
prev_num 上一頁的頁碼
next_num 下一頁的頁碼
has_prev 是否有上一頁
has_next 是否有下一頁
方法:
prev 上一頁的分頁對象
next 下一頁的分頁對象
iter_pages 是一個迭代器 返回所有的頁碼數(shù) 顯示不下則顯示 ...
緩存
安裝:
sudo pip3 install flask-cache
使用
from flask_cache import Cachce
cache = Cache(app,config={"CACHE_TYPE":"緩存類型"})
實例
extension.py
from flask_cache import Cache
cache = Cache(config={"CACHE_TYPE":'simple'})#實例化緩存 簡單的緩存
cache.init_app(app=app) #緩存
main.py
from App.extensions import cache
@main.route('/')
@cache.cached(timeout=120) #timeout 過期的時間
def index():
print('你能看到我?guī)状?。?!?)
緩存類型
- @cache.cached(timeout=120) #只是緩存頁面
- @cache.memoize(timeout=120) #會根據(jù)不同傳參進行不同的緩存
改正之后的緩存
from flask import Blueprint,render_template,request,current_app,redirect,url_for
from App.models import Posts
from App.extensions import cache
main = Blueprint('main',__name__)
@main.route('/')
# @cache.cached(timeout=120)
def index():
return redirect(url_for('main.main_cache',page=1))
@main.route('/show/<int:page>/')
@cache.memoize(timeout=120) #根據(jù)不同參數(shù)進行數(shù)據(jù)的緩存
def main_cache(page):
print('你能看到我?guī)状?。?!?)
# try:
# page = int(request.args.get('page', 1))
# except:
# page = 1
p = Posts.query.filter_by(pid=0).order_by(Posts.timestamp.desc()).paginate(page, current_app.config['CON_NUM'],False)
data = p.items # 當前頁的數(shù)據(jù)
return render_template('main/index.html', data=data, pagination=p)
清除緩存的辦法
(1) 在settings.py中 配置緩存時間
CACHE_DEFAULT_TIME
(2) 給裝飾器添加timeout參數(shù) 時間秒
@cache.cached(timeout=120)
@cache.memoize(timeout=120) #根據(jù)不同參數(shù)進行數(shù)據(jù)的緩存
(3) 清除所有緩存
cache.clear()
(4) 只清除 memoize的緩存
cache.delete_memoized(緩存的視圖函數(shù)名稱)
(5) 只清除 cached的緩存
cache.delete(key_prefix 緩存前綴)
表多對多的使用
添加多對多數(shù)據(jù)
u1 = User.query.get(1)
p1 = Posts.query.get(1)
#1號用戶 收藏 1號帖子
u1.favorites.append(p1)
查看多對多數(shù)據(jù)
u1.favorites.all() #查看用戶1收藏了那些帖子
p1.users.all() #查看帖子被哪些用戶收藏
刪除多對多數(shù)據(jù)
u1 = User.query.get(1)
p1 = Posts.query.get(1)
u1.favorites.remove(p1)
上線部署
(1) 安裝
-
在要安裝項目的目錄創(chuàng)建虛擬環(huán)境
virtualenv venv
source activate # 開啟虛擬開發(fā)環(huán)境模式
pip3 install uwsgi # 安裝uwsgi
(2) 配置
uwsgi配置文件支持很多格式,我采用.ini格式,命名為uconfig.ini具體內容如下:
[uwsgi]
# 外部訪問地址,可以指定多種協(xié)議,現(xiàn)在用http便于調試,之后用socket
socket = 0.0.0.0:8000 # uwsgi的監(jiān)聽端口
# 指向項目目錄
chdir = /home/xlg/test/
# flask啟動程序文件
wsgi-file = manage.py
# flask在manage.py文件中的app名
callable = app
plugins = python# 這行一定要加上,不然請求時會出現(xiàn)-- unavailable modifier requested: 0 --錯誤提示
# 處理器數(shù)
processes = 1
# 線程數(shù)
threads = 2
#狀態(tài)檢測地址
stats = 127.0.0.1:9191
(3) 安裝 nginx
Nginx:sudo apt-get install nginx
Nginx是輕量級、性能強、占用資源少,能很好的處理高并發(fā)的反向代理軟件。Ubuntu 上配置 Nginx 也是很簡單,不要去改動默認的 nginx.conf 只需要將/etc/nginx/sites-available/default文件替換掉就可以了。
新建一個 default 文件:
server{
listen 80; # 服務器監(jiān)聽端口
server_name 192.168.100.136; # 這里寫你的域名或者公網IP
location / {
uwsgi_pass 127.0.0.1:8000; # 轉發(fā)端口,需要和uwsgi配置當中的監(jiān)聽端口一致
include uwsgi_params; # 導入uwsgi配置
#uwsgi_param UWSGI_PYTHON /home/自己創(chuàng)建的目錄/venv; # Python解釋器所在的路徑(這里為虛擬環(huán)境)
uwsgi_param UWSGI_PYTHON /usr/bin/python3;
uwsgi_param UWSGI_CHDIR /home/xlg/test/;# # 自己創(chuàng)建的目錄 項目根目錄
uwsgi_param UWSGI_SCRIPT manage:app; # 指定啟動程序
#比如你測試用test.py文件,文件中app = Flask(name),那么這里就填 test:app
}
}
服務啟動
- sudo service start
- sudo service stop
- sudo service restart
指定配置文件,后臺運行 uwsgi, 這時再刷新一下之前打開的頁面,就可以看到應用正常運行了。
uwsgi uconfig.ini
訪問地址
192.168.100.136