Flask 應用程式開發中,檢視函式處理使用者請求並渲染樣板至關重要。本文將探討如何使用 URL 轉換器解析不同 URL 格式、實作產品分頁提升效能、運用 Jinja2 樣板引擎渲染資料,以及處理 XHR 請求實作非同步更新。同時,也將介紹如何建立裝飾器簡化 XHR 請求處理流程,並示範自定義錯誤處理機制以提供更友善的使用者經驗。透過這些技術,可以有效地管理應用程式的路由、資料呈現和使用者互動,建構更具彈性且使用者友善的網頁應用程式。
使用檢視(Working with Views)
實作 URL 路由和產品分頁
在某些情況下,我們需要以不同的方式解析 URL 的各個部分。例如,URL 可能包含整數部分、字串部分、特定長度的字串部分,以及斜線。我們可以使用 URL 轉換器來解析 URL 中的這些組合。在本章節中,我們將學習如何實作 URL 路由和產品分頁。
基礎 URL 轉換器
首先,我們來看看基本的 URL 轉換器。假設我們有一個 URL 路由定義如下:
@app.route('/test/<name>')
def get_name(name):
return name
在這個例子中,URL http://127.0.0.1:5000/test/Shalabh 將會解析出 Shalabh 並傳遞給 get_name 方法的 name 引數。這是預設的字串轉換器,不需要明確指定。
特定長度的字串轉換器
我們也可以指定字串的最小和最大長度。例如,若要解析包含國家程式碼或貨幣程式碼的 URL,可以這樣做:
@app.route('/test/<string(minlength=2,maxlength=3):code>')
def get_code(code):
return code
整數和浮點數轉換器
同樣地,我們可以解析整數值:
@app.route('/test/<int:age>')
def get_age(age):
return str(age)
也可以指定可接受的最小和最大值:
@app.route('/test/<int(min=18,max=99):age>')
def get_age(age):
return str(age)
實作產品分頁
接下來,我們來瞭解分頁的概念。在之前的章節中,我們建立了一個處理器來列出資料函式庫中的所有產品。如果我們有成千上萬的產品,一次性生成所有產品的列表將會非常耗時。分頁可以幫助我們建立更好的應用程式。
讓我們修改 products() 方法來支援分頁:
@catalog.route('/products')
@catalog.route('/products/<int:page>')
def products(page=1):
products = Product.query.paginate(page, 10).items
res = {}
for product in products:
res[product.id] = {
'name': product.name,
'price': product.price,
'category': product.category.name
}
return jsonify(res)
分頁方法解析
paginate() 方法接受四個引數並傳回一個 Pagination 物件的例項。這四個引數分別是:
page:目前要列出的頁面。per_page:每頁要列出的專案數量。error_out:如果找不到該頁的專案,則會中止並傳回 404 錯誤。若要防止這種行為,請將此引數設為False,這樣就會傳回一個空列表。max_per_page:如果指定了這個值,則per_page將被限制在同樣的值。
渲染到範本
在編寫檢視後,我們通常會想要在範本中渲染內容並從底層資料函式庫取得資訊。
首先,修改我們的檢視來渲染範本:
from flask import request, Blueprint, render_template
from my_app import db
from my_app.catalog.models import Product, Category
catalog = Blueprint('catalog', __name__)
@catalog.route('/')
@catalog.route('/home')
def home():
return render_template('home.html')
@catalog.route('/product/<id>')
def product(id):
# 渲染 product.html 範本,並傳遞 product 物件
product_obj = Product.query.get_or_404(id)
return render_template('product.html', product=product_obj)
內容解密:
render_template()方法:用於渲染指定的範本,並將範本變數傳遞給範本引擎。Product.query.get_or_404(id):根據指定的id從資料函式庫中查詢產品,如果找不到,則傳回 404 錯誤。- 範本變數:在
render_template()方法中傳遞的變數可以在範本中使用,例如product物件。
樣板渲染與XHR請求處理
在Flask應用程式中,樣板渲染和XHR(XMLHttpRequest)請求處理是兩個重要的功能。本篇文章將介紹如何使用Flask進行樣板渲染和處理XHR請求。
樣板渲染
樣板渲染是指將資料渲染到HTML樣板中,以產生最終的網頁內容。Flask使用Jinja2作為其樣板引擎。
產品相關樣板的渲染
在產品目錄應用程式中,我們定義了多個樣板來顯示產品資訊。以下是一個例子:
@catalog.route('/product/<id>')
def product(id):
product = Product.query.get_or_404(id)
return render_template('product.html', product=product)
在這個例子中,我們從資料函式庫中查詢產品資訊,然後將其渲染到product.html樣板中。
產品列表的渲染
我們還可以渲染產品列表,如下所示:
@catalog.route('/products')
@catalog.route('/products/<int:page>')
def products(page=1):
products = Product.query.paginate(page, 10)
return render_template('products.html', products=products)
在這個例子中,我們使用paginate方法來取得產品列表,並將其渲染到products.html樣板中。
XHR請求處理
XHR請求是一種用於在網頁上進行非同步請求的技術。Flask提供了簡便的方式來處理XHR請求。
檢查XHR請求
我們可以使用request.headers.get("X-Requested-With")來檢查請求是否為XHR請求。
from flask import request, render_template, jsonify
@catalog.route('/')
@catalog.route('/home')
def home():
if request.headers.get("X-Requested-With") == "XMLHttpRequest":
products = Product.query.all()
return jsonify({'count': len(products)})
return render_template('home.html')
在這個例子中,如果請求是XHR請求,我們傳回JSON資料;否則,我們渲染home.html樣板。
樣板範例
以下是一些樣板的範例:
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flask Framework Cookbook</title>
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet">
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="{{ url_for('catalog.home') }}">Flask Cookbook</a>
</div>
</div>
</div>
<div class="container">
{% block container %}{% endblock %}
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
</body>
</html>
product.html
{% extends 'home.html' %}
{% block container %}
<div class="top-pad">
<h1>{{ product.name }}<small> {{ product.category.name }}</small></h1>
<h4>{{ product.company }}</h4>
<h3>{{ product.price }}</h3>
</div>
{% endblock %}
products.html
{% extends 'home.html' %}
{% block container %}
<div class="top-pad">
{% for product in products.items %}
<div class="well">
<h2>
<a href="{{ url_for('catalog.product', id=product.id) }}">{{ product.name }}</a>
<small>$ {{ product.price }}</small>
</h2>
</div>
{% endfor %}
{% if products.has_prev %}
<a href="{{ url_for(request.endpoint, page=products.prev_num) }}">{{ "<< Previous Page" }}</a>
{% else %}
{{ "<< Previous Page" }}
{% endif %} |
{% if products.has_next %}
<a href="{{ url_for(request.endpoint, page=products.next_num) }}">{{ "Next page >>" }}</a>
{% else %}
{{ "Next page >>" }}
{% endif %}
</div>
{% endblock %}
內容解密:
此範例展示瞭如何在Flask中使用Jinja2樣板引擎來渲染產品資訊。透過繼承home.html,我們可以在product.html和products.html中重用相同的佈局。同時,products.html中使用了分頁功能,以顯示多個產品。
使用 XHR 請求處理資料
在現代 Web 應用程式中,使用 Ajax(Asynchronous JavaScript and XML)或 XHR(XMLHttpRequest)請求來非同步更新網頁內容已成為常見的做法。本文將探討如何在 Flask 應用程式中處理 XHR 請求,並提供一個簡單的範例來說明其實作方式。
修改範本以支援 XHR 請求
首先,我們需要修改 flask_catalog_template/my_app/templates/base.html 範本,以新增一個用於放置 JavaScript 程式碼的區塊。這個區塊將被用來處理 XHR 請求後的資料更新。
{% block scripts %}
{% endblock %}
處理 XHR 請求的檢視函式
接下來,在 flask_catalog_template/my_app/templates/home.html 範本中,我們將傳送一個 Ajax 請求到 home() 檢視函式。該函式將檢查請求是否為 XHR 請求,如果是,則從資料函式庫中取得產品數量並以 JSON 物件的形式傳回。
{% extends 'base.html' %}
{% block container %}
<h1>歡迎來到產品目錄首頁</h1>
<a href="{{ url_for('catalog.products') }}" id="catalog_link">
點選這裡檢視產品目錄
</a>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function(){
$.getJSON("/home", function(data) {
$('#catalog_link').append('<span class="badge">' + data.count + '</span>');
});
});
</script>
{% endblock %}
程式碼解析:
- 範本繼承:
home.html繼承自base.html,並覆寫了container和scripts區塊。 - Ajax 請求:當檔案載入完成後,傳送一個 GET 請求到
/home路由,並預期傳回 JSON 資料。 - 更新 DOM:將傳回的產品數量資料附加到
#catalog_link元素的後面。
使用裝飾器簡化 XHR 請求處理
檢查每個請求是否為 XHR 可能會降低程式碼的可讀性。為瞭解決這個問題,我們可以建立一個簡單的裝飾器來處理這項工作。
from functools import wraps
from flask import request, jsonify, render_template
def template_or_json(template=None):
def decorated(f):
@wraps(f)
def decorated_fn(*args, **kwargs):
ctx = f(*args, **kwargs)
if request.headers.get("X-Requested-With") == "XMLHttpRequest" or not template:
return jsonify(ctx)
else:
return render_template(template, **ctx)
return decorated_fn
return decorated
裝飾器解析:
template_or_json裝飾器:根據請求是否為 XHR 或是否提供了範本名稱,決定傳回 JSON 資料或渲染範本。
自定義錯誤處理器
每個應用程式都可能在某些時候向使用者丟擲錯誤。這些錯誤可能是由於使用者輸入了不存在的 URL(404)、應用程式過載(500)或是某些資源對特定使用者禁止存取(403)。Flask 提供了一個簡單易用的裝飾器來處理這些錯誤。
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
自定義錯誤頁面範例:
{% extends 'home.html' %}
{% block container %}
<div class="top-pad">
<h3>朋友,您似乎來到了不存在的地方。</h3>
<h4>請檢查您的地圖位置(URL)或回到 <a href="{{ url_for('catalog.home') }}">首頁</a></h4>
</div>
{% endblock %}
自定義錯誤解析:
@app.errorhandler(404):定義了一個處理 404 錯誤的函式,傳回一個渲染後的404.html範本。