資料函式庫遷移和快取是現代 Web 應用程式開發的關鍵環節。本文將引導你使用 Alembic 和 Flask-Migrate 管理 Flask 應用程式的資料函式庫結構變更,確保資料函式庫與程式碼同步,同時示範如何整合 Redis 快取,提升應用程式效能。透過實際程式碼範例,你將學習如何初始化遷移環境、產生遷移指令碼、套用資料函式庫更新,以及使用 Redis 儲存和讀取快取資料,進一步最佳化 Flask 應用程式。文章也涵蓋了 NoSQL 資料函式庫 MongoDB 的整合方式,提供更多元的資料函式庫選擇。除了程式碼實作,文章也說明瞭 Flask 中根據函式和類別的檢視撰寫方法,以及如何使用 MethodView 簡化類別檢視的開發流程。
資料函式庫遷移與快取實作:使用Alembic、Flask-Migrate與Redis最佳化Flask應用
在開發Flask應用程式時,資料函式庫遷移和快取機制是至關重要的技術。本篇文章將探討如何使用Alembic和Flask-Migrate進行資料函式庫遷移,以及如何利用Redis實作高效的資料快取。
資料函式庫遷移:Alembic與Flask-Migrate的協同工作
在進行資料函式庫遷移前,首先需要初始化遷移環境。執行以下指令:
$ flask db init
此步驟會在專案中建立遷移指令碼所需的目錄結構。
重要注意事項
為確保Flask應用程式能夠正確執行遷移指令,需要設定FLASK_APP環境變數,指向你的Flask應使用案例項:
export FLASK_APP="my_app.__init__.py"
或簡化為:
export FLASK_APP=my_app
模型變更與遷移
當資料模型發生變更後,例如在Product模型中新增company欄位:
class Product(db.Model):
# ... 其他欄位定義
company = db.Column(db.String(100))
執行以下指令以建立遷移指令碼:
$ flask db migrate
此指令會比較資料函式庫目前的結構與更新後的模型定義,自動生成遷移指令碼。輸出範例如下:
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added column 'product.company'
Generating <path/to/application>/flask_catalog/migrations/versions/2c08f71f9253_.py ... done
接著,執行以下指令以套用遷移:
$ flask db upgrade
輸出結果如下:
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade None -> 2c08f71f9253, empty message
資料函式庫遷移流程圖示
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Flask應用資料函式庫遷移與快取實作
package "Flask 資料庫遷移與快取" {
package "遷移工具" {
component [Alembic] as alembic
component [Flask-Migrate] as migrate
component [flask db init] as init
}
package "遷移流程" {
component [模型變更] as model
component [migrate 產生] as gen
component [upgrade 套用] as upgrade
}
package "快取整合" {
component [Redis] as redis
component [快取讀寫] as cache
component [MongoDB] as mongo
}
}
alembic --> migrate : 整合封裝
migrate --> init : 環境初始化
model --> gen : 偵測變更
gen --> upgrade : 套用遷移
redis --> cache : 高效存取
cache --> mongo : 多元資料庫
note right of migrate : FLASK_APP 設定\n遷移指令碼
note right of redis : 效能提升\n資料快取
@enduml使用Redis進行資料快取
為了提高應用效能,可以利用Redis作為快取儲存非永續性資料。例如,記錄最近瀏覽的產品資訊。
安裝與設定Redis
首先,安裝Redis客戶端函式庫:
$ pip install redis
接著,在my_app/__init__.py中建立Redis連線:
from redis import Redis
redis = Redis()
快取最近瀏覽產品
在產品詳情頁面檢視函式中,新增以下程式碼以快取產品資訊:
@catalog.route('/product/<id>')
def product(id):
product = Product.query.get_or_404(id)
product_key = 'product-%s' % product.id
redis.set(product_key, product.name)
redis.expire(product_key, 600) # 設定10分鐘過期
return 'Product - %s, $%s' % (product.name, product.price)
取得最近瀏覽產品列表
建立新的檢視函式以擷取快取中的產品資訊:
@catalog.route('/recent-products')
def recent_products():
keys_alive = redis.keys('product-*')
products = [redis.get(k).decode('utf-8') for k in keys_alive]
return jsonify({'products': products})
重點解析
- 資料函式庫遷移:使用Alembic和Flask-Migrate管理資料函式庫結構變更。
- 快取機制:利用Redis儲存非永續性資料,提高應用存取速度。
- 實作細節:詳細介紹了資料函式庫遷移和快取實作的步驟與程式碼範例。
使用 MongoDB 的 NoSQL 方式
在建構應用程式時,有時所使用的資料可能根本沒有結構,或者是半結構化的,又或者有一些資料的結構會隨著時間頻繁變更。在這種情況下,我們會避免使用關聯式資料函式庫(RDBMS),因為它會增加痛苦且難以擴充套件和維護。對於這種情況,最好使用 NoSQL 資料函式庫。
此外,由於目前流行的開發環境中快速發展的結果,我們不可能第一次就設計出完美的結構。NoSQL 提供了修改結構的彈性,而不會造成太大的麻煩。
在生產環境中,資料函式庫通常會隨著時間的推移而變得非常龐大。這會大大影響整個系統的效能。雖然有垂直和水平擴充套件技術可用,但有時它們可能會非常昂貴。在這種情況下,可以考慮使用 NoSQL 資料函式庫,因為它從一開始就為類別似的目的而設計。NoSQL 資料函式庫能夠在大型多叢集上執行,並處理以高速率生成的大量資料,這使得它們在處理傳統 RDBMS 的擴充套件問題時成為一個不錯的選擇。
在本配方中,我們將使用 MongoDB 來學習如何將 NoSQL 與 Flask 整合。
準備工作
有許多擴充功能可用於將 Flask 與 MongoDB 搭配使用。我們將使用 Flask-MongoEngine,因為它提供了良好的抽象層級,使得理解起來更容易。它可以使用以下指令安裝:
$ pip install flask-mongoengine
請記得執行 MongoDB 伺服器,以便建立連線。關於安裝和執行 MongoDB 的更多詳細資訊,請參考 http://docs.mongodb.org/manual/installation/。
操作步驟
首先,使用命令列手動建立 MongoDB 中的資料函式庫。讓我們將這個資料函式庫命名為 my_catalog:
>>> mongosh
Current Mongosh Log ID: 62fa8dtfd435df654150997b
Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+1.5.4
Using MongoDB: 6.0.0
Using Mongosh: 1.5.4
test> use my_catalog
switched to db my_catalog
以下是使用 MongoDB 重寫我們的目錄應用程式。第一個變化出現在我們的設定檔 my_app/__init__.py 中:
from flask import Flask
from flask_mongoengine import MongoEngine
app = Flask(__name__)
app.config['MONGODB_SETTINGS'] = {'DB': 'my_catalog'}
app.debug = True
db = MongoEngine(app)
from my_app.catalog.views import catalog
app.register_blueprint(catalog)
重點資訊
請注意,我們現在使用的是 MONGODB_SETTINGS 而不是通常的 SQLAlchemy 設定。在這裡,我們只需要指定要使用的資料函式庫名稱,即 my_catalog。
接下來,我們將使用 MongoDB 欄位建立一個 Product 模型。這通常發生在模型檔案 my_app/catalog/models.py 中:
import datetime
from my_app import db
class Product(db.Document):
created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
key = db.StringField(max_length=255, required=True)
name = db.StringField(max_length=255, required=True)
price = db.DecimalField()
def __repr__(self):
return '<Product %r>' % self.id
重點資訊
現在是時候看看用於建立前述模型的 MongoDB 欄位,以及它們與之前配方中使用的 SQLAlchemy 欄位的相似之處。在這裡,我們沒有 ID 欄位,而是使用 key 來儲存用於唯一識別記錄的唯一識別碼。另外,請注意建立模型時繼承的類別。在 SQLAlchemy 的情況下,它是 db.Model,而在 MongoDB 的情況下,它是 db.Document。這符合這些資料函式庫系統的工作方式。SQLAlchemy 使用傳統的 RDBMS,而 MongoDB 是一個 NoSQL 檔案資料函式庫系統。
以下是檢視檔案,即 my_app/catalog/views.py:
from decimal import Decimal
from flask import request, Blueprint, jsonify
from my_app.catalog.models import Product
catalog = Blueprint('catalog', __name__)
@catalog.route('/')
@catalog.route('/home')
def home():
return "Welcome to the Catalog Home."
@catalog.route('/product/<key>')
def product(key):
product = Product.objects.get_or_404(key=key)
return 'Product - %s, $%s' % (product.name, product.price)
@catalog.route('/products')
def products():
products = Product.objects.all()
res = {}
for product in products:
res[product.key] = {
'name': product.name,
'price': str(product.price),
}
return jsonify(res)
@catalog.route('/product-create', methods=['POST',])
def create_product():
name = request.form.get('name')
key = request.form.get('key')
price = request.form.get('price')
product = Product(
name=name,
key=key,
price=Decimal(price)
)
product.save()
return 'Product created.'
你會注意到它與根據 SQLAlchemy 的模型的檢視非常相似。只是從 MongoEngine 擴充功能呼叫的方法有一些不同,這些應該很容易理解。
工作原理
首先,使用 /product-create 端點新增產品到資料函式庫:
>>> res = requests.post('http://127.0.0.1:5000/product-create', data={'key': 'iphone-5s', 'name': 'iPhone 5S', 'price': '549.0'})
現在,透過存取 http://127.0.0.1:5000/products 端點來驗證產品的新增。以下是結果 JSON 值:
{
"iphone-5s": {
"name": "iPhone 5S",
"price": "549.00"
}
}
相關內容
請參考「建立基本產品模型」配方,以瞭解此應用程式的結構。
在Flask中處理檢視(Views)
撰寫根據函式的檢視與URL路由
在Flask中,撰寫根據函式的檢視與URL路由是最簡單的方式。我們只需撰寫一個方法並使用裝飾器(decorator)來定義端點。在本文中,我們將示範如何撰寫用於GET和POST請求的URL路由。
簡單的GET請求
以下是一個簡單的GET請求範例:
@app.route('/a-get-request')
def get_request():
bar = request.args.get('foo', 'bar')
return '一個簡單的Flask請求,其中foo是 %s' % bar
在這個範例中,我們檢查URL查詢是否包含名為foo的引數。如果有,我們在回應中顯示該值;否則,預設值為bar。
簡單的POST請求
POST請求與GET請求相似,但有一些差異:
@app.route('/a-post-request', methods=['POST'])
def post_request():
bar = request.form.get('foo', 'bar')
return '一個簡單的Flask請求,其中foo是 %s' % bar
在這個範例中,我們在路由中增加了一個額外的引數methods,並使用request.form來取得表單資料。
簡單的GET/POST請求
我們可以將GET和POST請求合併成一個檢視函式:
@app.route('/a-request', methods=['GET', 'POST'])
def some_request():
if request.method == 'GET':
bar = request.args.get('foo', 'bar')
else:
bar = request.form.get('foo', 'bar')
return '一個簡單的Flask請求,其中foo是 %s' % bar
內容解密:
request.args.get()用於取得GET請求中的查詢引數。request.form.get()用於取得POST請求中的表單資料。methods引數用於指定檢視函式支援的HTTP方法。
撰寫根據類別的檢視
Flask 0.7版本引入了可插拔檢視(pluggable views)的概念,使得我們可以以類別的形式撰寫檢視。在本文中,我們將示範如何建立根據類別的檢視。
簡單的GET請求
以下是一個簡單的GET請求範例:
from flask.views import View
class GetRequest(View):
def dispatch_request(self):
bar = request.args.get('foo', 'bar')
return '一個簡單的Flask請求,其中foo是 %s' % bar
app.add_url_rule('/a-get-request', view_func=GetRequest.as_view('get_request'))
簡單的GET/POST請求
我們可以建立一個支援GET和POST請求的類別檢視:
class GetPostRequest(View):
methods = ['GET', 'POST']
def dispatch_request(self):
if request.method == 'GET':
bar = request.args.get('foo', 'bar')
elif request.method == 'POST':
bar = request.form.get('foo', 'bar')
return '一個簡單的Flask請求,其中foo是 %s' % bar
app.add_url_rule('/a-request', view_func=GetPostRequest.as_view('a_request'))
內容解密:
View類別是Flask提供的基礎類別,用於建立可插拔檢視。dispatch_request方法是檢視的核心,用於處理請求並傳回回應。methods屬性用於指定檢視支援的HTTP方法。
使用MethodView簡化類別檢視
Flask提供了MethodView類別,使得我們可以更簡潔地建立類別檢視。以下是一個範例:
from flask.views import MethodView
class GetPostRequest(MethodView):
def get(self):
bar = request.args.get('foo', 'bar')
return '一個簡單的Flask請求,其中foo是 %s' % bar
def post(self):
bar = request.form.get('foo', 'bar')
return '一個簡單的Flask請求,其中foo是 %s' % bar
app.add_url_rule('/a-request', view_func=GetPostRequest.as_view('a_request'))
內容解密:
MethodView類別提供了更簡潔的方式來建立類別檢視。- 我們可以為每個HTTP方法定義單獨的方法(例如
get、post等)。 as_view方法用於將類別檢視轉換為可用的檢視函式。