文章目錄
1、標準類視圖及使用
在前面,咱們定義視圖都是經過route裝飾器裝飾函數來定義的,通常稱之爲視圖函數。除了這種方式,還能夠基於類實現。
類視圖支持繼承,可是類視圖不能跟函數視圖同樣經過裝飾器添加路由,須要經過app.add_url_rule(url_rule,view_func)
來註冊。
css
1.添加url映射規則的其餘方法嘗試
在以前的代碼中,都是經過@app.route裝飾器來實現url與視圖函數的映射的,除了這種方法外,咱們也能夠經過Flask對象的add_url_rule()
方法來定義映射規則。
測試(新建一個Python文件class_view.py)以下:
html
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return '首頁' def profile(): return '我的中心' # 添加url規則 app.add_url_rule('/profile/', view_func=profile) if __name__ == '__main__': app.run(debug=True)
顯示:
顯然,add_url_rule()
能夠定義路由規則。
python
add_url_rule()
方法有一個參數endpoint,表示結束點,指定後url_for()方法中傳入的就再也不是視圖函數名了,而是指定的endpoint,至關於給url取了一個名字。
經過請求上下文函數能夠輸出url_for的結果,測試以下:
json
from flask import Flask,url_for app = Flask(__name__) @app.route('/') def index(): print(url_for('personal')) return '首頁' def profile(): return '我的中心' app.add_url_rule('/profile/', endpoint='personal', view_func=profile) if __name__ == '__main__': app.run(debug=True)
此時訪問http://127.0.0.1:5000/在控制檯會打印/profile/
。
此時不能再在url_for()
方法中傳入’profile’參數,不然會報錯。
flask
2.標準類視圖
標準類視圖繼承自flask.views.View
,而且類視圖中必須重寫dispatch_request()
方法,這個方法相似於視圖函數,會返回一個基於Response或者其子類的對象。類視圖定義後,須要經過add_url_rule()
方法和url進行映射,還須要在as_view()
方法中指定該url的名稱,方便url_for()
函數調用。
定義類視圖以下:
服務器
from flask import Flask, url_for, views app = Flask(__name__) @app.route('/') def index(): print(url_for('personal')) return '首頁' def profile(): return '我的中心' class ListView(views.View): def dispatch_request(self): return 'class view' def demo(self): return '測試' app.add_url_rule('/profile/', endpoint='personal', view_func=profile) app.add_url_rule('/list/', endpoint='list', view_func=ListView.as_view('list')) if __name__ == '__main__': app.run(debug=True)
訪問http://127.0.0.1:5000/list/,顯示:
此時,若是要用url_for()
方法來獲取URL,須要注意:
若是add_url_rule()
中指定了endpoint參數,url_for()
中傳的參數就應該是endpoint參數的值;
若是沒有指定,就是as_view()
方法的值。
markdown
在定義視圖類時,必需要重寫dispatch_request()
方法,不然會拋出異常,以下:
能夠查看源碼views.py:
app
def dispatch_request(self): """Subclasses have to override this method to implement the actual view function code. This method is called with all the arguments from the URL rule. """ raise NotImplementedError()
顯然,若是不重寫dispatch_request()
方法,會直接拋出NotImplementedError錯誤。dom
再嘗試用類視圖傳遞Json數據:ide
from flask import Flask, url_for, views, jsonify app = Flask(__name__) @app.route('/') def index(): return '首頁' def profile(): return '我的中心' class JsontView(views.View): def get_response(self): raise NotImplementedError() def dispatch_request(self): response = self.get_response() return jsonify(response) class ListJsonView(JsontView): def get_response(self): return {'username': 'Corley'} app.add_url_rule('/listjson/', view_func=ListJsonView.as_view('listjson')) if __name__ == '__main__': app.run(debug=True)
顯示:
顯然,此時返回頁面的是json數據。
返回公共變量測試:
from flask import Flask, url_for, views, jsonify, render_template app = Flask(__name__) @app.route('/') def index(): return '首頁' class LoginView(views.View): def dispatch_request(self): self.context = { 'name': 'Corley' } return render_template('login.html', **self.context) class RegisterView(views.View): def dispatch_request(self): self.context = { 'name': 'Corley' } return render_template('register.html', **self.context) app.add_url_rule('/login/', view_func=LoginView.as_view('login')) app.add_url_rule('/register/', view_func=RegisterView.as_view('register')) if __name__ == '__main__': app.run(debug=True)
在templates目錄下建立login.html和register.html,login.html以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陸頁面</title> </head> <body> <h1>這是登陸頁面</h1> <h2>{{ name }}</h2> </body> </html>
register.html以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>註冊頁面</title> </head> <body> <h1>這是註冊頁面</h1> <h2>{{ name }}</h2> </body> </html>
顯示:
顯然,能夠正常訪問登陸和註冊頁面,可是兩個視圖類中含有相同的變量,能夠進行優化:
from flask import Flask, url_for, views, jsonify, render_template app = Flask(__name__) @app.route('/') def index(): return '首頁' class BaseView(views.View): """ 保存公共變量 """ def __init__(self): super().__init__() self.context = { 'name': 'Corley' } class LoginView(BaseView): def dispatch_request(self): return render_template('login.html', **self.context) class RegisterView(BaseView): def dispatch_request(self): return render_template('register.html', **self.context) app.add_url_rule('/login/', view_func=LoginView.as_view('login')) app.add_url_rule('/register/', view_func=RegisterView.as_view('register')) if __name__ == '__main__': app.run(debug=True)
將公有變量存儲到BaseView,其餘類視圖再繼承自BaseView。
2、基於調度方法的視圖
Flask中除了標準類視圖,還有一種類視圖flask.views.MethodView,對每一個HTTP方法執行不一樣的函數,映射到對應方法的同名方法上。
測試以下:
from flask import Flask, url_for, views, jsonify, render_template, request app = Flask(__name__) @app.route('/') def index(): return '首頁' class BaseView(views.View): """ 保存公共變量 """ def __init__(self): super().__init__() self.context = { 'name': 'Corley' } class LoginView(views.MethodView): # 當客戶端經過get方法訪問的時候執行 def get(self): return render_template('login.html') # 當客戶端經過post方法訪問的時候執行 def post(self): name = request.form.get('name') password = request.form.get('password') if name == 'Corley' and password == "123": return '登陸成功!!!' else: error = '帳號或密碼錯誤⚠' return render_template('login.html', error=error) class RegisterView(BaseView): def dispatch_request(self): return render_template('register.html') app.add_url_rule('/login/', view_func=LoginView.as_view('login')) app.add_url_rule('/register/', view_func=RegisterView.as_view('register')) if __name__ == '__main__': app.run(debug=True)
顯示:
顯然,經過get和post請求都能實現登陸的效果,代碼還能夠進行必定的優化:
from flask import Flask, url_for, views, jsonify, render_template, request app = Flask(__name__) @app.route('/') def index(): return '首頁' class BaseView(views.View): """ 保存公共變量 """ def __init__(self): super().__init__() self.context = { 'name': 'Corley' } class LoginView(views.MethodView): def get(self, error=None): return render_template('login.html', error=error) def post(self): name = request.form.get('name') password = request.form.get('password') if name == 'Corley' and password == "123": return '登陸成功!!!' else: error = '帳號或密碼錯誤⚠' return self.get(error) class RegisterView(BaseView): def dispatch_request(self): return render_template('register.html') app.add_url_rule('/login/', view_func=LoginView.as_view('login')) app.add_url_rule('/register/', view_func=RegisterView.as_view('register')) if __name__ == '__main__': app.run(debug=True)
運行效果與前者相同。
類視圖的一個缺陷就是比較難用裝飾器來裝飾,好比須要作權限驗證的時候。此時能夠在類視圖中定義一個默認屬性decorators,用於存儲裝飾器。之後每次調用這個類視圖的時候,就會執行這個裝飾器。
在類外添加裝飾器,限制只有登陸以後才能訪問我的中心,以下:
from flask import Flask, url_for, views, jsonify, render_template, request app = Flask(__name__) @app.route('/') def index(): return '首頁' def login_required(func): '''裝飾器,登陸以後才能訪問我的中心''' def wrapper(*args, **kwargs): username = request.args.get('username') if username: return func(*args, **kwargs) else: return '請先登陸再訪問' return wrapper @app.route('/profile/') @login_required def profile(): return '我的中心' if __name__ == '__main__': app.run(debug=True)
顯示:
很明顯,只有登陸以後才能夠正常訪問到我的中心。
經過在decorators列表中添加裝飾器實如今類中使用裝飾器,測試以下:
from flask import Flask, url_for, views, jsonify, render_template, request app = Flask(__name__) @app.route('/') def index(): return '首頁' def login_required(func): '''裝飾器,登陸以後才能訪問我的中心''' def wrapper(*args, **kwargs): username = request.args.get('username') if username: return func(*args, **kwargs) else: return '請先登陸再訪問' return wrapper class ProfileView(views.View): # 在類中用裝飾器 decorators = [login_required] def dispatch_request(self): return '我的中心頁面' app.add_url_rule('/profile/', view_func=ProfileView.as_view('profile')) if __name__ == '__main__': app.run(debug=True)
3、Flask藍圖的基本使用
以前,咱們的代碼都是放在一個Python文件裏的,不一樣的功能模塊都在一個文件中實現,代碼量較少時尚可,隨着業務需求的複雜化和代碼的增多,項目逐漸變大,會顯得很臃腫,也沒有條理層次感,這顯然不是一個合理的結構。此時就須要定義多個文件(夾),不一樣的文件(夾)放不一樣的功能模塊,此時藍圖能夠優雅地實現這種需求。
定義了藍圖文件以後,須要在主程序中經過app.register_blueprint()
方法將這個藍圖註冊進url映射中。
簡單使用示例以下:
先建立項目根目錄,如blue_demo,在下面建立Python文件blue_flask.py以下
from flask import Flask from blueprints.news import news_bp from blueprints.book import book_bp app = Flask(__name__) app.register_blueprint(news_bp) app.register_blueprint(book_bp) @app.route('/') def index(): return '這是首頁' if __name__ == '__main__': app.run(debug=True)
再建立blueprints目錄,下邊建立news.py和book.py,news.py以下:
from flask import Blueprint news_bp = Blueprint('news', __name__) # 第一個參數是藍圖名稱,通常是當前的Python文件名,第二個參數是藍圖所在的包或模塊,通常用__name__變量 @news_bp.route('/news/') def news(): return '新聞頁面'
book.py以下:
from flask import Blueprint book_bp = Blueprint('book', __name__) # 第一個參數是藍圖名稱,通常是當前的Python文件名,第二個參數是藍圖所在的包或模塊,通常用__name__變量 @book_bp.route('/book/') def book(): return '圖書列表' @app.route('/book/detail/<bid>') def book_detail(bid): return '當前圖書ID:%s' % bid
運行blue_flask.py以後,顯示:
顯然,基本功能已經實現。
心啊在訪問/news/
、/book/
,都是在執行news.py、book.py中的視圖函數,從而實現了項目的模塊化。
若是在blue_flask.py中不能導入blueprints中的文件或者出現警告信息,能夠將當前目錄設爲根目錄,以下:
此時再導入就會出現提示,導入後也不會報錯。
Blueprint對象在實例化時,還能夠傳入一個參數url_prefix,即url前綴,會給當前文件下全部的路由加上指定的前綴,如上面的book.py能夠改爲
from flask import Blueprint book_bp = Blueprint('book', __name__, url_prefix='/book') # 第一個參數是藍圖名稱,通常是當前的Python文件名,第二個參數是藍圖所在的包或模塊,通常用__name__變量 @book_bp.route('/') def news(): return '圖書列表' @book_bp.route('/detail/<bid>') def book_detail(bid): return '當前圖書ID:%s' % bid
這與以前的效果是同樣的。
4、Flask藍圖尋找文件和url_for()尋找路由
1.Flask藍圖尋找模板文件
Flask藍圖默認不設置任何模板文件的路徑,將會在項目的templates中尋找模板文件。
也能夠設置其餘的路徑,在構造函數Blueprint中有一個template_folder參數能夠設置模板的路徑。
修改news.py以下:
from flask import Blueprint,render_template news_bp = Blueprint('news', __name__, template_folder='blue_templates') @news_bp.route('/news/') def news(): return render_template('news.hmtl')
修改templates中的news.html以下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>新聞</title> </head> <body> <h1>這是模板中的新聞首頁</h1> </body> </html>
在blueprints中建立目錄blue_templates,在下面建立news.html以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>新聞</title> </head> <body> <h1>這是藍圖中的新聞首頁</h1> </body> </html>
運行後,顯示:
顯然,顯示的是項目中的templates目錄中的模板,進一步能夠總結:
在藍圖中定義的視圖函數在尋找模板時,首先會在項目的默認模板目錄templates中尋找是否有指定的模板,若是存在則進行渲染,不存在則繼續看藍圖文件中初始化Blueprint對象時是否傳入了template_folder參數來指定模板目錄,若是傳了則在參數指定的目錄中尋找,找到則進行渲染,不然報錯,若是沒有指定template_folder參數參數也會直接報錯。
在實際項目中,通常直接將模板文件放在默認的模板目錄templates下。
2.Flask藍圖尋找靜態文件
默認不設置任何靜態文件路徑,Jinja2會在項目的static文件夾中尋找靜態文件。
也能夠設置其餘的路徑,在初始化藍圖的時候,Blueprint構造函數有一個參數static_folder能夠指定靜態文件的路徑。
static_folder能夠是相對路徑(相對藍圖文件所在的目錄),也能夠是絕對路徑。配置完藍圖後,在模板中引用靜態文件應該使用藍圖名.static
。
測試以下:
先在blue_demo目錄下建立static目錄,並在static目錄下建立news.css以下:
body{ background: pink; }
templates目錄下的news.html導入css文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="{{ url_for('static', filename='news.css') }}"> <title>新聞</title> </head> <body> <h1>這是模板中的新聞首頁</h1> </body> </html>
同時在blueprints目錄下建立blue_static目錄,下面建立news.css以下:
body{ background: blue; }
運行後能夠看到:
顯然,此時加載的模板文件是templates目錄中的news.html,使用的樣式文件是static目錄下的news.css。
要想使用blue_static目錄下的樣式文件,須要改動,首先須要在Blueprint初始化時指定靜態文件的文件夾:
from flask import Blueprint,render_template news_bp = Blueprint('news', __name__, static_folder='blue_static') @news_bp.route('/news/') def news(): return render_template('news.html')
templates目錄下的news.html導入css文件的路徑修改以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="{{ url_for('news.static', filename='news.css') }}"> <title>新聞</title> </head> <body> <h1>這是模板中的新聞首頁</h1> </body> </html>
顯示:
此時加載的樣式文件便是blueprints目錄下的靜態文件夾中的樣式文件。
3.藍圖中使用url_for()方法獲取路由
在存在藍圖的狀況下使用使用url_for()
方法,不能直接給路由對應的函數名,須要在前面加上定義該路由所在的藍圖名字,即初始化藍圖時的第一個參數,通常即爲該函數所在的Python文件的文件名,使用的格式是藍圖名稱.視圖函數名稱
。
blue_flask.py以下:
from flask import Flask, url_for from blueprints.news import news_bp from blueprints.book import book_bp app = Flask(__name__) app.register_blueprint(news_bp) app.register_blueprint(book_bp) @app.route('/') def index(): print(url_for('news.news')) print(url_for('book.book_detail', bid=2)) return '這是首頁' if __name__ == '__main__': app.run(debug=True)
book.py以下:
from flask import Blueprint, url_for book_bp = Blueprint('book', __name__, url_prefix='/book') @book_bp.route('/') def book(): print(url_for('book.book_detail', bid=3)) return '圖書列表' @book_bp.route('/detail/<bid>') def book_detail(bid): return '當前圖書ID:%s' % bid
顯示:
此時查看控制檯打印以下:
127.0.0.1 - - [17/Apr/2020 17:41:17] "GET / HTTP/1.1" 200 - /news/ /book/detail/2 /book/detail/3 127.0.0.1 - - [17/Apr/2020 17:41:27] "GET /book/ HTTP/1.1" 200 -
顯然,此時獲得了視圖函數對應的路由。
5、Flask實現子域名
子域名(subdomain)在域名系統等級中,屬於更高一層域的域,好比,mail.example.com
和calendar.example.com
是example.com
的兩個子域名,而example.com
則是頂級域.com
的子域名。
子域名在不少網站中都會用到,好比對於一個網站xxx.com,咱們能夠定義一個子域名cms.xxx.com來做爲該網站內容管理系統的網址。
在Flask中,子域名通常也是經過藍圖來實現的,在以前建立藍圖的時候添加了一個參數url_prefix來指定url前綴,好比url_prefix='/user'
,咱們能夠經過/user/來訪問user下的地址。但使用子域名則不一樣,須要在主程序中配置SERVER_NAME,除此以外,還須要在電腦的系統文件hosts中進行配置。
示例以下:
在blueprints目錄下建立cms.py文件以下:
from flask import Blueprint cms_bp = Blueprint('cms', __name__, subdomain='cms') @app.route('/') def cms(): return 'CMS頁面'
在主程序中註冊和配置服務器名稱以下:
from flask import Flask, url_for from blueprints.news import news_bp from blueprints.book import book_bp from blueprints.cms import cms_bp app = Flask(__name__) app.register_blueprint(news_bp) app.register_blueprint(book_bp) app.register_blueprint(cms_bp) # 配置服務器名 app.config["SERVER_NAME"] = 'corley.com:5000' @app.route('/') def index(): print(url_for('news.news')) print(url_for('book.book_detail', bid=2)) return '這是首頁' if __name__ == '__main__': app.run(debug=True)
此時還不能直接訪問子域名,由於子域名不支持在IP前直接添加,即cms.127.0.0.1的形式是無效的,因此須要在電腦系統文件hosts中添加域名解析,即將127.0.0.1映射到一個域名,才能訪問子域名,Windows下操做以下:
在C盤中依次打開目錄:C:→Windows→System32→drivers→etc,找到hosts文件並編輯,在該文件後面添加域名映射以下:
# localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # ::1 localhost 127.0.0.1 corley.com 127.0.0.1 cms.corley.com
最後兩行即爲要添加的內容,將127.0.0.1映射到corley.com,此時訪問corley.com即訪問本地迴環地址127.0.0.1。
此時開啓服務,能夠在控制檯看到:
* Serving Flask app "blue_flask" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: on * Restarting with stat * Debugger is active! * Debugger PIN: 260-774-670 * Running on http://corley.com:5000/ (Press CTRL+C to quit)
即此時訪問站點須要訪問http://corley.com:5000/,此時也不能再訪問http://127.0.0.1:5000/了(除非你其餘應用開啓了此服務),此時訪問示例以下:
顯然,能夠訪問到子域名http://cms.corley.com:5000/。