一、基礎
1,首先是虛擬環境的配置
pip install virtualenv -i https://pypi.doubanio.com/simple # 豆瓣源安裝虛擬環境
mkdir falsk-venv cd falsk-venv virtualenv venv # 在當前目錄下床架一個目錄,表示虛擬環境的目錄名為venv,包含了Python可執行文件,以及pip庫的一個備份
當然如果本機存在多個版本的Python,可以選擇一個Python,可以選擇一個Python解釋器,在指定之前,必須將flask-venv目錄下整個文件夾都刪掉再使用
virtualenv -p C:Python27\python.exe venv # -p 用來指定Python解釋器程序的路徑
那如何激活使用虛擬環境呢?
在cmd里面輸入
dir activate # 激活虛擬環境 deactivate # 退出虛擬環境 rmvirtualenv flask-venv # 刪除虛擬環境
2,Flask 快速上手
使用pycharm來創建
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
有人會問啊,為什么非要加這個
if __name__ == '__main__':
其實原因是在python中,所有沒有縮進的代碼都會被執行,__name__是Python的內建函數,指的是當前模塊的名稱,,每個模塊都有這個屬性,但__name__是可以變化的,如果某個模塊直接被運行那么__name__ = '__main__', 條件為真,但是當模塊被導入的時候就為假,也就是app.run()不會被執行。
和Django不同的是flask的路由是以裝飾器的形式加載所需要執行視圖函數的上方
調試:
django的開啟調試是在settings中的DEBUG=True,而flask簡單很多,在app.run()中加入debug=True即可
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run(debug=True) # 開啟調試
flask開啟調試好處是什么呢?
1,遇到程序有bug,會在控制台輸出具體的錯誤信息,否則只會報籠統的應用服務器錯誤。
2,自動重啟
當然app.run中可以加的參數還有很多,比如設定host,port
另外說一句:默認的port就是5000
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)
二、URL傳遞參數
Flask傳遞參數的語法是'/參數/'
注意點:①參數需要放在一對<>②視圖函數中需要設置與URL中相同的參數名
from flask import Flask app = Flask(__name__) @app.route('/user/<name>') def hello_world(name): return f'Hello World!{name}' if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)
像這個就是沒有指定數據的類型的,那么默認的就是string
from flask import Flask app = Flask(__name__) @app.route('/user/<int:id>') def hello_world(id): return f'Hello World!{id}' if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)
像這樣就是指定接收的參數類型為int類型,傳遞參數str或者float都是不行的
三、URL的反轉
我們知道Django的URL反轉模板語言中使用的是這樣{% url ‘name’ %} 通過別名的方式,視圖函數中是這樣
from django.shortcuts import reverse reverse('article:article_detail', kwargs={'id': article_id, 'slug': slug})
但是在flask中,URL的反轉卻很有意思直接使用url_for
from flask import Flask, url_for app = Flask(__name__) @app.route('/user') def hello_world(id): reverse_path = url_for('hello_world') return f'Hello World!{reverse_path}' if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)
使用URL的反轉,使用的是
from flask import url_for
最簡單的做法即使以視圖函數的名稱作為參數,就像上面例子得到的就是/user
三、模板引擎jinja2
因為jinja2和Django模板引擎基本一樣,所以基礎的就不講了,直接開干
比方說我們隨便在templates目錄下新建一個html文件user.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>{{ name }}</div> </body> </html>
app.py文件代碼如下:
from flask import Flask, render_template app = Flask(__name__) @app.route('/user') def hello_world(): name = '先生' return render_template('user.html', name=name) if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)
和Django的不同的是:Django使用render(request, '模板')來進行返回
Flask通過render_template()函數來實現模板的渲染。要使用jinjia2模板就是必須用
from flask import render_template
當然參數量過多的時候**locals()
from flask import Flask, render_template app = Flask(__name__) @app.route('/user') def hello_world(): name = '先生' return render_template('user.html', **locals()) if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)
而Django不要加**
上面的都是太簡單了,整點有難度的,舉個栗子,自定義過濾器(本質上過濾器就是轉換函數)
我們都知道django的過濾器需要在各自的app下新建一個templatetags,然后在模板中導入縮寫的py文件
那么Flask怎么做的呢?
通過調用add_template_filter方法來實現。該方法第一個參數是函數名,第二個參數是自定義過濾器的名稱。
user.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>{{ name | index_class }}</div> </body> </html>
app.py
from flask import Flask, render_template app = Flask(__name__) @app.route('/user', methods=['GET', 'POST']) def hello_world(): name = '先生' return render_template('user.html', **locals()) def do_index_class(index): # 定義函數 return index + '牛逼****' app.add_template_filter(do_index_class, 'index_class') if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=8000)
對於如果post上傳文件
就需要使用
f = request.files['filesname'] 和django類似
f.save(‘path’,‘name’)即可
宏的定義及使用:
Jinja 2中的宏功能類似Python中的函數,可以傳參,但是不能有返回值。我們可以將一些經常用到的代碼片段放在宏中,然后把一些不固定的值抽取出來作為一個變量。
宏的聲明:
<!--定義宏--> {% macro input (name, type='text', value='') -%} <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}"> {%- endmacro %}
上方的代碼定義了一個宏,定義宏要加macro,結束要加上endmacro標志。宏的名稱時input
他們有三個參數,分別是name、type、value。后兩個參數有默認值。我們可以試用表達式來調用這個宏
{{ input('username') }}
{{ input('password', type='password') }}
看一個完整的實例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>{{ name | index_class }}</div> <div> <!--定義宏--> {% macro input (name, type='text', value='') -%} <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}"> {%- endmacro %} <p>用戶名:{{ input('username') }}</p> <p>密碼:{{ input('password', type='password') }}</p> <p>登錄:{{ input('submit', type='submit', value='登錄') }}</p> </div> </body> </html>

宏的導入:
一個宏可以被不同的模板使用,因此我們想到了一個東西,抽取為公共模塊,在需要使用的時候導入即可
導入的方法呢也和Python類似,import嘛
因為我們在上面已經寫了一個宏了
那我們就導入
方式1:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% import 'user.html' as user %} <p>用戶名:{{ user.input('username') }}</p> <p>密碼:{{ user.input('password', type='password') }}</p> <p>登錄:{{ user.input('submit', type='submit', value='登錄') }}</p> </body> </html>
方式二:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% from 'user.html' import input %} <p>用戶名:{{ input('username') }}</p> <p>密碼:{{ input('password', type='password') }}</p> <p>登錄:{{ input('submit', type='submit', value='登錄') }}</p> </body> </html>
效果是完全一樣的
include的使用:
和django的include類似:
宏文件可以引用其他宏,可以使用include語句可以把一個模板引入到另一個模板中,類似於把一個模板的代碼復制到另外一個模板的指定位置。
footer.hrml
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class="footer">這是網頁尾部</div> </body> </html>
header.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class="header">這是網頁頭部</div> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .header { width: 100%; height: 40px; margin: 20px 20px; } .footer { width: 100%; height: 40px; margin: 20px 20px; } .content { width: 100%; height: 40px; margin: 20px 20px; } </style> </head> <body> {% from 'user.html' import input %} <p>用戶名:{{ input('username') }}</p> <p>密碼:{{ input('password', type='password') }}</p> <p>登錄:{{ input('submit', type='submit', value='登錄') }}</p> {% include "header.html" %} <div class="content">這是網頁內容</div> {% include "footer.html" %} </body> </html>
頁面效果:

set 和with語句的使用:
set語句是在模板中定義變量並賦予值,在整個模板范圍都有效
with關鍵字是在定義變量並賦值的同事,限制with定義變量的作用范圍
{% set a = '上山打老虎' %}
{% set b = ['上山打老虎', ('天青色等煙雨', '而我在等你')] %}
{{ a }}
{{ b }}
{% with bat = 99 %}
{{ bat }}
{% endwith %}

靜態文件的加載:
<script type="text/javascript" src="static/js/jquery-3.3.1/jquery-3.3.1.js"></script> <script src="{{ url_for('static', filename='js/jquery-3.3.1/jquery-3.3.1.js') }}"></script> <script src="{{ url_for('static', filename='images/dog.jpg') }}"></script> <link rel="stylesheet" href="{{ url_for('static', filename='css/dog.css') }}">
推薦使用{{url_for}}的方法
模板的繼承:
和Django一樣,在需要繼承的子模板中extends即可
父模板user.html
{% block baby %}
{% endblock %}
子模版
{% extends 'user.html' %}
{% block baby %}
{{ super() }}
{% endblock %}
注意:如果想在一個block中調用其他的block代碼可以通過{{self.其他block名稱()}}實現
{% self.baby() %}
四、Flask視圖高級技術
在Flask框架中,默認使用@app.route裝飾器:類似這樣
@app.route('/user') def hello_world(): name = '先生' return render_template('user.html', **locals())
上述代碼中,視圖函數為hello_word是如此建立URL和視圖之間的關系的
我們想要反轉得到url,url_for("hello_word")反轉得到URL,實際上flask可以像Django一樣添加別名,那就是用endpoint參數
@app.route('/user', endpoint='hello') def hello_world(): name = '先生' return render_template('index.html', **locals())
一旦我們加上endpoint參數,在使用url_for()反轉時就不能使用視圖函數名了,而要使用我們定義的URL別名
那這個app.route的裝飾器本質是什么呢?
我們看一下源碼

那么這個add_url_rule怎么使用呢?
換句話說,如何通過add_url_rule方法來實現和上面相同的效果呢?
# @app.route('/user', endpoint='hello') def hello_world(): name = '先生' return render_template('index.html', **locals()) app.add_url_rule('/user', endpoint='hello', view_func=hello_world) # endpoint參數依舊可以不填,但是view_func必須是對應的函數名
類視圖:
那我們可能會想,既然裝飾器更加方便為什么還需要用add_url_rule方法?
因為類的視圖函數需要通過其來進行注冊
flask的類視圖一般分為標准類視圖,基於調度方法的類視圖
①標准類視圖:
1, 必須繼承flask.views.View
2, 必須實現dispatch_request方法,請求過來之后,都會執行這個方法。這個方法的返回值相當於之前的視圖函數,也必須返回Response或者子類的對象,或者是字符串、元祖
3, 必須通過app.add_rule(rule, endpoint, view_func)來做URL與視圖的映射。view_func參數需要使用as_view類方法來轉換、
4, 如果指定endpoint,url_for反轉時必須使用別名。反之則使用as_view(視圖名稱)指定的視圖名稱來作為反轉
優點:
使用類視圖的好處是支持繼承,可以把一些共性的東西放在父類中,其他子類可以繼承,但是類視圖不能和函數視圖一樣,需要通過app.add_url_rule來注冊
例子:
網站里面需要在多個不同頁面都放置一個廣告,使用類視圖函數如何實現?
from flask import Flask, render_template, views app = Flask(__name__) class Bgs(views.View): def __init__(self): super().__init__() self.context = { "bgs": "我是固定牛皮廯" } class Index(Bgs): def dispatch_request(self): # 使用dispatch_request()方法,定義類視圖函數 return render_template('index.html', **self.context) class Index2(Bgs): def dispatch_request(self): # 使用dispatch_request()方法,定義類視圖函數 return render_template('index.html', **self.context) # 定義view_func必須經過as_view方法 app.add_url_rule(rule='/', endpoint='index', view_func=Index.as_view('Index')) app.add_url_rule(rule='/index2', endpoint='index2', view_func=Index2.as_view('Index2'))
②基於方法的類視圖:
利用視圖函數實現不同請求不同的邏輯時比較復雜,和django的類視圖方法類似。也就是flask.views.MethodView,對每一個Http方法執行不同的函數
from flask import Flask, render_template, views, request app = Flask(__name__) @app.route('/user', endpoint='hello') def hello_world(): name = '先生' return render_template('index.html', **locals()) class LoginView(views.MethodView): # 定義LoginView類 def get(self):
name = request.args['username'] return render_template("index.html") def post(self): username = request.form.get("username") password = request.form.get("password") if username == 'zhou' and password == '123': return "登陸成功" else: return "登錄失敗" app.add_url_rule('/login', endpoint='hello', view_func=LoginView.as_view('loginview'))
Flask的藍圖:
flask為了降低模塊之間的耦合性,設計了藍圖
優點: 可以極大的簡化大型應用,並為擴展提供集中的注冊入口。Blueprint對象與Flask應用對象的工作方式類似,但是不是一個真正的應用
舉個栗子:
新建三個文件,分別為app.py b.py c.py
from flask import Flask, render_template import b, c app = Flask(__name__) @app.route('/user', endpoint='hello') def hello_world(): name = '先生' return render_template('index.html', **locals()) app.register_blueprint(b.b_list) # 將b模塊中的b_list注冊到app app.register_blueprint(c.c_list) # 將c模塊中的c_list注冊到app
b.py
# -*- coding:UTF-8 -*- __autor__ = 'zhouli' __date__ = '2019/11/3 18:14' from flask import Blueprint b_list = Blueprint("b", __name__) """ 創建一個Blueprint對象,第一個參數可以當做改Blueprint對象的姓名 在一個app中,姓名不能與其余的Blueprint對象姓名重復 第二個參數用於初始化 """ @b_list.route("/b") def b(): return "b"
藍圖的主要目的:實現各個模塊的視圖函數寫在不同的py文件中。在主視圖中導入分路視圖的模塊,並且注冊藍圖對象。
Flask處理表單:
1,使用flask處理通用表單
①
pip install flask-wtf # Flask-WTF 提供對所有Form表單免受跨站請求偽造(CSRF)
②在flask根目錄下面新增config.py配置文件,要啟用CSRF保護,需要定義兩個變量
CSRF_ENABLED = True SECRET_KEY = ‘x1x2x3x4x5x6’ # 自定義
其中SECRET_KEY用來建立加密的令牌,用於驗證Form表單的提交,在自己編寫對應的應用程序的時候,可以設置的較為復雜些,那這樣攻擊者就難以猜到密鑰值
在app.py中添加
