協助開發者快速開發應用程序的一套功能代碼.開發者只須要按照框架約定要求,在指定位置寫本身的業務邏輯代碼.html
內部邏輯後續交流中會使用代碼的方式進行互相交流學習。python
穩定性和可擴展性強,提升開發效率,減小開發難度,使開發簡便.經常使用web框架有:flask和Django.web
Flask:Flask 自己至關於一個內核,其餘幾乎全部的功能都要用到擴展(郵件擴展Flask-Mail,用戶認證Flask-Login,數據庫Flask-SQLAlchemy),都須要用第三方的擴展來實現。其 WSGI 工具箱採用 Werkzeug(路由模塊),模板引擎則使用 Jinja2。這兩個也是 Flask 框架的核心。數據庫
1.導入Flask類:json
from flask import Flask
2.建立程序實例:flask
app = Flask(__name__) // 它指向程序所在的包
3.裝飾路由:api
@app.route('/')
def demo():
return "hello world" //裝飾器的做用是將路由映射到視圖函數demo,通俗的說就是URL的綁定
4.啓動web服務器瀏覽器
if __name__ == "__main__":
app.run() //在程序運行過程當中,程序實例中會使用url_map將裝飾器路由和視圖函數的對應關係保存起來.以前給你簡單的點擊過源碼。不是重點能夠忽略!
import__name__ //Flask程序所在的包(模塊),傳__name__便可,其可決定Flask文件在訪問靜態文件時查找的路徑.
static_url_path //靜態文件訪問路徑,能夠不傳,默認爲/static_folder.
static_folder //靜態文件存儲的文件夾,能夠不傳,默認爲static.注意此處沒有加s,命名規範
template_folder //模板文件存儲的文件夾,能夠不傳,默認爲templates.
# 配置對象,裏面定義須要給 APP 添加的一系列配置
class Config(object):
DEBUG = True
# 建立 Flask 類的對象,指向程序所在的包的名稱
app = Flask(__name__)
# 從配置對象中加載配置
app.config.from_object(Config)
建立配置文件 config.ini
,在配置文件中添加配置 DEBUG = True安全
# 建立 Flask 類的對象,指向程序所在的包的名稱
app = Flask(__name__)
# 從配置文件中加載配置
app.config.from_pyfile('config.ini')
編輯運行的相關配置服務器
# 建立 Flask 類的對象,指向程序所在的包的名稱
app = Flask(__name__)
# 加載指定環境變量名稱所對應的相關配置
app.config.from_envvar('FLASKCONFIG') //name值爲FLASKCONFIG,value值爲config.ini
app.debug = True
app.config['DEBUG'] = True
能夠指定運行的主機IP地址,端口,是否開啓調試模式
app.run(host="0.0.0.0", port=5000, debug = True)
路由:客戶端把請求發送給Web服務器,Web服務器再把請求發送給程序實例。程序實例須要知道對每一個URL請求運行哪些代碼,因此保存了一個 URL 到 Python 函數的映射關係。處理URL和函數之間關係的程序稱路由。
在Flask定義路由的最簡便方式,是使用程序實例提供的app.route裝飾器,把裝飾的函數註冊爲路由.
路由的本質路由的本質,是URL 綁定, @app.route() 裝飾器用於把一個函數綁於一個URL上,如上,/綁定了index()函數,/user綁定了hello_user()函數,這個例子將 index() 函數註冊爲程序根 '/' 地址。訪問 http://localhost:5000/ 後,會觸發服務器執行 index() 函數,這個函數的返回值稱爲響應,是客戶端接收到的內容。
像 index() 這樣的函數稱爲視圖函數。視圖函數返回的響應能夠是包含 HTML 的簡單字符串,也能夠是複雜的表單
路由查找方式
視圖函數做用: 處理業務邏輯和返回響應內容;
同一路由指向兩個不一樣的函數,在匹配過程當中,至上而下依次匹配
from flask import Flask
app = Flask(name)
@app.route('/')
def hello():
return '<h1>hello world</h1>'
@app.route('/')
def hello_2017():
return '<h1>hello 2017</h1>'
if __name__ == "__main__"
app.run()
因此上面路由 / 輸出的結果爲 hello 函數的結果
1.指定路由地址
# 指定訪問路徑爲 demo1
@app.route('/demo1')
def demo1():
return 'demo1'
2.路由傳遞的參數默認當作string處理.
# 路由傳遞參數 //有時咱們須要將同一類 URL 映射到同一個視圖函數處理;
@app.route('/user/<user_id>')
def user_info(user_id):
return 'hello %s' % user_id
3.路由傳遞的參數也能夠指定參數的類型.
# 路由傳遞參數
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
//這裏指定int,尖括號中的內容是動態的,在此暫時能夠理解爲接受 int 類型的值,實際上 int 表明使用 IntegerConverter去處理 url 傳入的參數;
4.請求方式 //Flask中默認的請求方式爲"get", 自帶OPTIONS和HEAD
@app.route('/demo2', methods=['GET', 'POST'])
def demo2():
# 直接從請求中取到請求方式並返回
return request.method
使用 Flask 寫一個接口時候須要給客戶端返回 JSON 數據,在 Flask 中能夠直接使用 jsonify 生成一個 JSON 的響應
//返回json
@app.route('/demo4')
def demo4():
json_dict = {
"user_id": 10,
"user_name": "laowang"
} //json.dumps通常是text/html格式,jsonify作了封裝指定響應內容.
return jsonify(json_dict) //jsonify會指定響應內容的數據格式(告訴客戶端我返回給你的數據格式是什麼)
# 重定向 (重定向到黑馬官網)
@app.route('/demo5')
def demo5():
return redirect('http://www.zhanghaibin.com')
//重定向到本身寫的視圖函數
//能夠直接填寫本身 url 路徑
//也可使用 url_for 生成指定視圖函數所對應的 url
@app.route('/demo1')
def demo1():
return 'demo1'
# 重定向
@app.route('/demo5')
def demo5():
return redirect(url_for('demo1'))
//重定向到帶有參數的視圖函數
//在 url_for 函數中傳入參數
# 路由傳遞參數
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
# 重定向
@app.route('/demo5') //url_for:取到指定視圖函數所對應的路由URL,而且能夠攜帶參數
def demo5():
# 使用 url_for 生成指定視圖函數所對應的 url
return redirect(url_for('user_info', user_id=100))
//在 Flask 中,能夠很方便的返回自定義狀態碼,以實現不符合 http 協議的狀態碼,例如:status code: 666
@app.route('/demo6')
def demo6():
return '狀態碼爲 666', 666
在 web 開發中,可能會出現限制用戶訪問規則的場景,那麼這個時候就須要用到正則匹配,根據本身的規則去限定請求參數再進行訪問 ;
具體實現步驟爲:
1.1導入轉換器基類:在 Flask 中,全部的路由的匹配規則都是使用轉換器對象進行記錄
1.2自定義轉換器:自定義類繼承於轉換器基類
1.3添加轉換器到默認的轉換器字典中
1.4使用自定義轉換器實現自定義匹配規則
1.1導入轉換器基類
from werkzeug.routing import BaseConverter
自定義轉換器
1.2自定義正則轉換器
class RegexConverter(BaseConverter):
def __init__(self, url_map, *args):
super(RegexConverter, self).__init__(url_map)
# 將接受的第1個參數看成匹配規則進行保存
self.regex = args[0]
添加轉換器到默認的轉換器字典中,並指定轉換器使用時名字爲: re
app = Flask(__name__)
1.3將自定義轉換器添加到轉換器字典中,並指定轉換器使用時名字爲: re
app.url_map.converters['re'] = RegexConverter
1.4使用轉換器去實現自定義匹配規則
當前此處定義的規則是:3位數字
@app.route('/user/<re("[0-9]{3}"):user_id>')
def user_info(user_id):
return "user_id 爲 %s" % user_id
繼承於自定義轉換器以後,還能夠實現 to_python 和 to_url 這兩個函數去對匹配參數作進一步處理 :
to_python:
該函數參數中的 value 值表明匹配到的值,可輸出進行查看
匹配完成以後,對匹配到的參數做最後一步處理再返回
to_url:
在使用 url_for 去獲取視圖函數所對應的 url 的時候,會調用此方法對 url_for 後面傳入的視圖函數參數作進一步處理
from flask import Flask
from flask import redirect
from flask import url_for
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
"""自定義正則的轉換器"""
# regex = "[0-9]{6}"
def __init__(self, url_map, *args):
super(RegexConverter, self).__init__(url_map)
# 取到第1個參數,給regex屬性賦值
self.regex = args[0]
class ListConverter(BaseConverter):
"""本身定義轉換器"""
regex = "(\\d+,?)+\\d$" //匹配到的是列表字符串1,2,3,4
def to_python(self, value):
"""當匹配到參數以後,對參數作進一步處理以後,再返回給視圖函數中"""
return value.split(',')
def to_url(self, value):
"""使用url_for的時候,對視圖函數傳的參數進行處理,處理完畢以後以便可以進行路由匹配"""
result = ','.join(str(v) for v in value)
return result
app = Flask(__name__)
# 將本身的轉換器添加到默認的轉換器列表中
app.url_map.converters["re"] = RegexConverter
app.url_map.converters["list"] = ListConverter
@app.route('/')
def index():
return 'index'
# 規則:/user/6位數字 [0-9]{6}
# 自定義轉換器
@app.route('/user/<re("[0-9]{6}"):user_id>')
def demo1(user_id):
return '用戶id是 %s' % user_id
@app.route('/users/<list:user_ids>')
def demo2(user_ids):
# 若是才能在視圖函數中接收到的 user_ids 就是一個列表
return "用戶的id列表是 %s" % user_ids
@app.route('/demo3')
def demo3():
return redirect(url_for('demo2', user_ids=[1, 3, 4, 5]))
if __name__ == '__main__':
app.run(debug=True)
系統自帶轉換器共六種:
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
abort 方法 : abort(500) 注意:拋出狀態碼的話,只能拋出 HTTP 協議的錯誤狀態碼 .
errorhandler 裝飾器
註冊一個錯誤處理程序,當程序拋出指定錯誤狀態碼的時候,就會調用該裝飾器所裝飾的方法
參數:
code_or_exception – HTTP的錯誤狀態碼或指定異常
@app.errorhandler(500)
def internal_server_error(e):
return '服務器搬家了'
在請求開始時,創建數據庫鏈接 ;
在請求開始時,根據需求進行權限校驗;
在請求結束時,指定數據的交互格式;
爲了讓每一個視圖函數避免編寫重複功能的代碼,Flask提供了通用設施的功能,即請求鉤子 .
請求鉤子是經過裝飾器的形式實現,Flask支持以下四種請求鉤子 :
from flask import Flask
from flask import abort
app = Flask(__name__)
# 在第一次請求以前調用,能夠在此方法內部作一些初始化操做
@app.before_first_request
def before_first_request():
"""在第一次請求以前會訪問該函數"""
print("before_first_request")
# 在每一次請求以前調用,這時候已經有請求了,可能在這個方法裏面作請求的校驗
# 若是請求的校驗不成功,能夠直接在此方法中進行響應,直接return以後那麼就不會執行視圖函數
@app.before_request
def before_request():
"""在每次請求以前都會調用"""
print("before_request")
# if 請求不符合條件:
# return "laowang"
# 在執行完視圖函數以後會調用,而且會把視圖函數所生成的響應傳入,能夠在此方法中對響應作最後一步統一的處理
@app.after_request
def after_request(response):
"""在請求以後會調用,而且函數裏面接受一個參數:響應,還須要將響應進行返回"""
print("after_request")
response.headers["Content-Type"] = "application/json"
return response
# 請每一次請求以後都會調用,會接受一個參數,參數是服務器出現的錯誤信息
@app.teardown_request
def teardown_request(error):
"""在請求以後會執行,若是請求的函數報有異常,會把具體異常傳入到此函數"""
print("teardown_request")
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
Flask有兩大核心:Werkzeug和Jinja2
Werkzeug實現路由、調試和Web服務器網關接口
Werkzeug是一個遵循WSGI協議的python函數庫
- 其內部實現了不少Web框架底層的東西,好比request和response對象;
- 與WSGI規範的兼容;支持Unicode;
- 支持基本的會話管理和簽名Cookie;
- 集成URL請求路由等。
Werkzeug庫的 routing 模塊負責實現 URL 解析。不一樣的 URL 對應不一樣的視圖函數,routing模塊會對請求信息的URL進行解析,匹配到URL對應的視圖函數,執行該函數以今生成一個響應信息。
routing模塊內部有:
Rule類
用來構造不一樣的URL模式的對象,路由URL規則
Map類
存儲全部的URL規則和一些配置參數
BaseConverter的子類
負責定義匹配規則
MapAdapter類
負責協調Rule作具體的匹配的工做
request 就是flask中表明當前請求的 request 對象,其中一個請求上下文變量(理解成全局變量,在視圖函數中直接使用能夠取到當前本次請求)
經常使用的屬性以下:
屬性 | 說明 | 類型 |
---|---|---|
data | 記錄請求的數據,並轉換爲字符串 | * |
form | 記錄請求中的表單數據 | MultiDict |
args | 記錄請求中的查詢參數 | MultiDict |
cookies | 記錄請求中的cookie信息 | Dict |
headers | 記錄請求中的報文頭 | EnvironHeaders |
method | 記錄請求使用的HTTP方法 | GET/POST |
url | 記錄請求的URL地址 | string |
files | 記錄請求上傳的文件 | * |
代碼實現
//get請求用args接
//post請求用form接
//原始數據用data屬性,post表單用form屬性,files文件必須用post中的form-data,raw沒有格式限制.
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/args')
def args():
username = request.args.get("username")
password = request.args.get("password")
print("%s %s" % (username, password))
return "get請求是OK"
@app.route('/form', methods=['POST'])
def form():
username = request.form.get("username")
password = request.form.get("password")
print("%s %s" % (username, password))
return "post表單請求用form接收OK"
@app.route('/upload', methods=['POST'])
def upload():
file = request.files.get('pic')
file.save('./static/aaa.png')
return 'success'
@app.route('/data', methods=['POST'])
def data():
data = request.data
print(data)
return 'data返回的是json格式的數據'
if __name__ == '__main__':
app.run(debug=True) //開啓自動調試模式debug = True
爲何要狀態保持? 由於http是一種無狀態的協議,瀏覽器請求服務器是無狀態的 .
狀態保持的目的是在一段時間內跟蹤請求者的狀態,能夠實現跨頁面訪問當前請求者的數據.
無狀態協議:
協議對於事務處理沒有記憶能力
對同一個 url 請求沒有上下文關係
每次的請求都是獨立的,它的執行狀況和結果與前面的請求和以後的請求是無直接關係的,它不會受前面的請求應答狀況直接影響,也不會直接影響後面的請求應答狀況
服務器中沒有保存客戶端的狀態,客戶端必須每次帶上本身的狀態去請求服務器
人生若只如初見
無狀態:指一次用戶請求時,瀏覽器、服務器沒法知道以前這個用戶作過什麼,每次請求都是一次新的請求。
無狀態緣由:瀏覽器與服務器是使用 socket 套接字進行通訊的,服務器將請求結果返回給瀏覽器以後,會關閉當前的 socket 鏈接,並且服務器也會在處理頁面完畢以後銷燬頁面對象。
需求: 有時須要保持下來用戶瀏覽的狀態,好比用戶是否登陸過,瀏覽過哪些商品等;
實現狀態保持主要有兩種方式:
在客戶端存儲信息使用Cookie
在服務器端存儲信息使用Session
cookie 和 session的區別:須要你進行資料的整理和查詢
以前給你強調過的兩種技術
上下文:至關於一個容器,保存了 Flask 程序運行過程當中的一些信息。
Flask中有兩種上下文,請求上下文和應用上下文:分別包含哪幾種,做用,這點很重要,理解csrf會用到。
命令行操做(自行了解便可)
視圖函數的兩個做用: 處理業務邏輯和返回響應內容;
模板,它的做用即承擔視圖函數的另外一個做用,即返回響應內容。
模板實際上是一個包含響應文本的文件,其中用佔位符(變量)表示動態部分,告訴模板引擎其具體的值須要從使用的數據中獲取;
使用真實值替換變量,再返回最終獲得的字符串,這個過程稱爲「渲染";
Flask是使用 Jinja2 這個模板引擎來渲染模板
使用模板的優勢: 視圖函數只負責業務邏輯和數據處理(業務邏輯方面)
**而模板則將取到視圖函數的數據結果進行展現(視圖展現方面)**
**代碼結構清晰,耦合度低**
Jinja2:是 Python 下一個被普遍應用的模板引擎,是由Python實現的模板語言,他的設計思想來源於 Django 的模板引擎,並擴展了其語法和一系列強大的功能,其是Flask內置的模板語言。模板語言:是一種被設計來自動生成文檔的簡單文本格式,在模板語言中,通常都會把一些變量傳給模板,替換模板的特定位置上預先定義好的佔位變量名。
{{ }} 來表示變量名,這種 {{}} 語法叫作變量代碼塊
Jinja2 模版中的變量代碼塊能夠是任意 Python 類型或者對象,只要它可以被 Python 的 str() 方法轉換爲一個字符串就能夠;
用 {% %} 定義的控制代碼塊,能夠實現一些語言層次的功能,好比循環或者if語句
渲染模版函數Flask提供的 render_template 函數封裝了該模板引擎render_template 函數的第一個參數是模板的文件名,後面的參數都是鍵值對,表示模板中變量對應的真實值。
@app.route('/')
def index():
my_list = [
{
"id": 1,
"value": "我愛工做"
},
{
"id": 2,
"value": "工做令人快樂"
},
{
"id": 3,
"value": "沉迷於工做沒法自拔"
},
{
"id": 4,
"value": "日漸消瘦"
},
{
"id": 5,
"value": "張海斌,愛學習"
}
]
return render_template('control.html',
my_list_dict=my_list) //注意模板的文件名,還有接收數據的變量名
模板中代碼
<body>
{% for item in my_list_dict if item.id != 5 %}
{% if loop.index == 1 %}
<li style="{{ item.value }}</li>
{% elif loop.index == 2 %}
<li style="{{ item.value }}</li>
{% elif loop.index == 3 %}
<li style="{{ item.value }}</li>
{% else %}
<li style="{{ item.value }}</li>
{% endif %} //if判斷必須有endif結尾
{% endfor %} //for循環必須有endfor結尾
</body>
1.在項目下建立 templates 文件夾,用於存放全部的模板文件,並在目錄下建立一個模板html文件temp_demo1.html
2.設置 templates 文件夾屬性以便可以在代碼中有智能提示
3.設置 html 中的模板語言,以便在 html 有智能提示
4.建立視圖函數,將該模板內容進行渲染返回
5.代碼中傳入字符串,列表,字典到模板中
過濾器的本質就是函數。有時候咱們不只僅只是須要輸出變量的值,咱們還須要修改變量的顯示,甚至格式化、運算等等,而在模板中是不能直接調用 Python 中的某些方法,那麼這就用到了過濾器。
過濾器的使用方式爲:變量名 | 過濾器。
在 jinja2 中,過濾器是能夠支持鏈式調用的
{{ "hello world" | reverse | upper }}
{{'haibin' | reverse | upper }}<br>
{{ 'haibin' | reverse }}<br>
{{ my_str }}<br>
默認是安全的,
{{ my_str | escape }}<br>
//加safe後會解析js代碼,不加safe不會解析,會直接顯示傳入的js字符串腳本
{{ my_str | safe }}<br>
safe:禁用轉義
capitalize:把變量值的首字母轉成大寫,其他字母轉小寫
lower:把值轉成小寫
upper:把值轉成大寫
title:把值中的每一個單詞的首字母都轉成大寫
reverse:字符串反轉
過濾器的本質是函數。當模板內置的過濾器不能知足需求,能夠自定義過濾器。自定義過濾器有兩種實現方式:
一種是經過Flask應用對象的 add_template_filter 方法
經過裝飾器來實現自定義過濾器
重要:自定義的過濾器名稱若是和內置的過濾器重名,會覆蓋內置的過濾器。
經過調用應用程序實例的 add_template_filter 方法實現自定義過濾器。該方法第一個參數是函數名,第二個參數是自定義的過濾器名稱:
def do_listreverse(li):
# 經過原列表建立一個新列表
temp_li = list(li) //這樣不會對原來的列表進行修改
# 將新列表進行返轉
temp_li.reverse()
return temp_li
app.add_template_filter(do_listreverse,'lireverse')
用裝飾器來實現自定義過濾器。裝飾器傳入的參數是自定義的過濾器名稱。
@app.template_filter('lireverse')
def do_listreverse(li):
# 經過原列表建立一個新列表
temp_li = list(li)
# 將新列表進行返轉
temp_li.reverse()
return temp_li
對宏(macro)的理解:
把它看做 Jinja2 中的一個函數,它會返回一個模板或者 HTML 字符串爲了不反覆地編寫一樣的模板代碼,出現代碼冗餘,能夠把他們寫成函數以進行重用須要在多處重複使用的模板代碼片斷能夠寫入單獨的文件,再包含在全部模板中,以免重複
對模板繼承(block)的理解:
模板繼承是爲了重用模板中的公共內容。通常Web開發中,繼承主要使用在網站的頂部菜單、底部。這些內容能夠定義在父模板中,子模板直接繼承,而不須要重複書寫。
對包含(include)的理解:
Jinja2模板中,除了宏和繼承,還支持一種代碼重用的功能,叫包含(Include)。它的功能是將另外一個模板整個加載到當前模板中,並直接渲染。包含在使用時,若是包含的模板文件不存在時,程序會拋出TemplateNotFound異常,能夠加上 ignore missing 關鍵字。若是包含的模板文件不存在,會忽略這條include語句。
後續進行後面的內容:
orm
數據庫的遷移操做
重點內容均在後續,前面內容須要搞明白.....
# 以上均爲劉亞傑幫忙整理,在此表示感謝。
2019-07-03 07:44:28