在現代網頁應用程式中,日期和時間的正確顯示至關重要,尤其對於跨時區的應用。本文將介紹如何使用 Moment.js 在前端處理日期時間格式,確保不同時區的使用者都能看到正確的顯示結果。同時,我們也會探討如何在 Flask 後端使用 SQLAlchemy 建立資料函式庫模型,並利用 Flask-Migrate 和 Alembic 進行資料函式庫遷移。透過前端和後端的協作,我們可以構建更穩健且易於維護的應用程式。對於前端,我們將使用 Moment.js 函式庫處理日期時間格式,藉由在客戶端瀏覽器進行格式化,可以更精確地顯示使用者所在地時間,同時也能降低伺服器的負擔。後端部分,我們將使用 SQLAlchemy 建立資料函式庫模型,包含產品和類別等實體,並建立它們之間的關聯性,例如一對多關係。為了有效管理資料函式庫架構的變更,我們將使用 Flask-Migrate 和 Alembic 進行資料函式庫遷移,這對於迭代開發和版本控制至關重要。

日期與時間格式化技術在前端的應用

在開發跨地區的應用程式時,正確處理日期和時間的顯示是一個重要的挑戰。由於不同使用者可能位於不同的時區,因此直接在伺服器端進行日期和時間的格式化可能會導致顯示不正確。更聰明的做法是將這個處理過程轉移到客戶端,即瀏覽器端。瀏覽器始終知道使用者當前的時區,因此能夠正確地處理日期和時間資訊。這種做法還能減少應用程式伺服器的不必要負擔。在本文中,我們將瞭解如何使用 Moment.js 函式庫來實作這一點。

Moment.js 簡介與基本使用

Moment.js 是一個流行的 JavaScript 函式庫,用於處理日期和時間。要在我們的應用程式中使用 Moment.js,首先需要下載並將 moment.min.js 檔案放置在 static/js 資料夾中。可以從 Moment.js 官方網站下載該檔案。然後,在 HTML 檔案中加入以下陳述式來參照它:

<script src="{{ url_for('static', filename='js/moment.min.js') }}"></script>

Moment.js 的基本使用如下所示,這些範例可以在瀏覽器的 JavaScript 控制檯中執行:

>>> moment().calendar(); 
"今天 上午11:09"

>>> moment().endOf('day').fromNow(); 
"13小時內"

>>> moment().format('LLLL'); 
"2022年7月24日 星期日 上午11:10"

程式碼解析:

  1. moment().calendar():根據目前時間輸出相對時間描述,如「今天 上午11:09」。
  2. moment().endOf('day').fromNow():計算從目前時間到當天結束的相對時間,如「13小時內」。
  3. moment().format('LLLL'):按照指定格式輸出日期和時間,如「2022年7月24日 星期日 上午11:10」。

在 Flask 應用程式中使用 Moment.js

要在 Flask 應用程式中使用 Moment.js,需要建立一個 Python 包裝器,並透過 Jinja 環境變數來使用它。

首先,建立一個名為 momentjs 的類別,如下所示:

from markupsafe import Markup

class momentjs(object):
    def __init__(self, timestamp):
        self.timestamp = timestamp

    # 包裝器用於呼叫 moment.js 方法
    def render(self, format):
        return Markup(
            "<script>\ndocument.write(moment(\"%s\").%s);\n</script>" % (
                self.timestamp.strftime("%Y-%m-%dT%H:%M:%S"),
                format
            )
        )

    # 格式化時間
    def format(self, fmt):
        return self.render("format(\"%s\")" % fmt)

    def calendar(self):
        return self.render("calendar()")

    def fromNow(self):
        return self.render("fromNow()")

內容解密:

  • __init__ 方法:初始化 momentjs 物件,接受一個 timestamp 引數。
  • render 方法:用於生成呼叫 Moment.js 的 JavaScript 程式碼,將日期格式化後傳給前端。
  • formatcalendarfromNow 方法:分別對應 Moment.js 中的不同格式化方法,用於輸出指定格式的日期和時間。

接下來,在 app.py 檔案中,將建立的 momentjs 類別設定為 Jinja 環境的全域變數:

# 設定 Jinja 範本全域變數
app.jinja_env.globals['momentjs'] = momentjs

現在,可以在範本中使用 momentjs 類別,如下所示。確保 timestamp 是 JavaScript 日期物件的例項:

<p>目前時間: {{ momentjs(timestamp).calendar() }}</p>
<br/>
<p>時間: {{ momentjs(timestamp).format('YYYY-MM-DD HH:mm:ss') }}</p>
<br/>
<p>相對時間: {{ momentjs(timestamp).fromNow() }}</p>

內容解密:

  • momentjs(timestamp).calendar():輸出相對時間描述。
  • momentjs(timestamp).format('YYYY-MM-DD HH:mm:ss'):按照指定格式輸出日期和時間。
  • momentjs(timestamp).fromNow():輸出相對目前時間的時間描述。

資料函式庫建模在 Flask 中的應用

本章節將探討在 Flask 應用程式中如何與資料函式庫系統互動,包括定義模型和查詢資料函式庫以檢索和儲存資料。Flask 的設計使其能夠支援任何資料函式庫系統。在本章節中,我們將重點介紹如何使用 SQLAlchemy 建立物件關聯對映(ORM)層,以及如何使用 NoSQL 資料函式庫系統。

使用 SQLAlchemy 建立資料函式庫例項

SQLAlchemy 是一個功能強大的 Python SQL 工具包,提供了 ORM 功能,使得 SQL 操作更加靈活和 Pythonic。首先,需要安裝 Flask-SQLAlchemy 擴充功能:

$ pip install flask-sqlalchemy

然後,組態應用程式以指定資料函式庫的位置:

app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL') or 'sqlite:///' + os.path.join(basedir, 'app.db')

SQLAlchemy 提供了一個名為 Model 的類別,用於定義應用程式的資料模型。

使用SQLAlchemy建立資料函式庫例項與基本產品模型

建立SQLAlchemy資料函式庫例項

在Flask應用程式中使用SQLAlchemy來建立資料函式庫例項是一個常見的做法。首先,我們需要組態Flask應用程式以指向特定的資料函式庫位置。然後,建立一個SQLAlchemy物件,這個物件將處理所有與ORM(物件關聯對映)相關的活動。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

with app.app_context():
    db.create_all()

內容解密:

  1. 我們首先匯入必要的模組,包括FlaskSQLAlchemy
  2. 組態Flask應用程式的SQLALCHEMY_DATABASE_URI以指定資料函式庫的位置。在此例中,我們使用SQLite資料函式庫,資料函式庫檔案將被儲存在/tmp/test.db
  3. 建立一個SQLAlchemy物件db,並將其與Flask應用程式app繫結。
  4. 使用with app.app_context():來建立應用程式上下文,以便能夠在其中執行db.create_all(),這將建立資料函式庫中的所有表格。

建立基本產品模型

接下來,我們將建立一個產品模型,用於在目錄部分展示產品資訊。這個模型將包含產品的ID、名稱和價格。

from my_app import db

class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    price = db.Column(db.Float)

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __repr__(self):
        return '<Product %d>' % self.id

內容解密:

  1. 匯入db物件,這是之前建立的SQLAlchemy例項。
  2. 定義一個名為Product的類別,該類別繼承自db.Model,使其成為一個ORM模型。
  3. Product類別中定義三個屬性:idnameprice,分別對應於資料函式庫表中的欄位。
  4. __init__方法用於初始化產品物件,需要提供名稱和價格。
  5. __repr__方法傳回產品物件的字串表示形式,主要用於除錯。

處理產品檢視

為了能夠與產品模型互動,我們需要定義一些檢視函式。

from flask import request, jsonify, Blueprint
from my_app import db
from my_app.catalog.models import Product

catalog = Blueprint('catalog', __name__)

@catalog.route('/product/<id>')
def product(id):
    product = Product.query.get_or_404(id)
    return 'Product - %s, $%s' % (product.name, product.price)

@catalog.route('/products')
def products():
    products = Product.query.all()
    res = {}
    for product in products:
        res[product.id] = {
            'name': product.name,
            'price': str(product.price)
        }
    return jsonify(res)

@catalog.route('/product-create', methods=['POST',])
def create_product():
    name = request.form.get('name')
    price = request.form.get('price')
    product = Product(name, price)
    db.session.add(product)
    db.session.commit()
    return 'Product created.'

內容解密:

  1. 匯入必要的模組和物件,包括requestjsonifyBlueprintdbProduct模型。
  2. 定義一個名為catalog的藍圖,用於組織相關的檢視函式。
  3. /product/<id>路由根據提供的ID查詢產品,如果找到,則傳回產品資訊;否則,傳回404錯誤。
  4. /products路由傳回所有產品的JSON表示形式。
  5. /product-create路由用於建立新的產品,需要透過POST請求提供產品名稱和價格。

測試與驗證

首先,透過存取http://127.0.0.1:5000/products來確認初始狀態下沒有產品。然後,使用Python的requests函式庫傳送POST請求到http://127.0.0.1:5000/product-create以建立一個新產品。再次存取http://127.0.0.1:5000/products,應該能夠看到新建立的產品資訊。

import requests
requests.post('http://127.0.0.1:5000/product-create', data={'name': 'iPhone 5S', 'price': '549.0'})

這個過程展示瞭如何在Flask應用程式中使用SQLAlchemy建立資料函式庫例項、定義模型以及實作基本的CRUD(建立、讀取、更新、刪除)操作。

資料函式庫模型設計與遷移在Flask中的應用

在開發Flask應用程式時,資料函式庫模型的設計與遷移是至關重要的環節。本篇文章將探討如何在Flask中建立具有關聯性的資料函式庫模型,並使用Alembic和Flask-Migrate進行資料函式庫遷移。

建立具有關聯性的類別模型

在前面的章節中,我們建立了一個簡單的產品模型。然而,在實際應用中,資料函式庫表之間通常存在著各種關聯,如一對一、一對多、多對一或多對多。本章節將透過一個例子來理解這些關聯。

如何實作

假設我們想要建立產品類別,每個類別可以有多個產品,但每個產品只能屬於一個類別。我們將透過修改之前的應用程式來實作這一點。

首先,修改models.py檔案以新增Category模型,並對Product模型進行一些修改:

from my_app import db

class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    price = db.Column(db.Float)
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
    category = db.relationship('Category', backref=db.backref('products', lazy='dynamic'))

    def __init__(self, name, price, category):
        self.name = name
        self.price = price
        self.category = category

    def __repr__(self):
        return '<Product %d>' % self.id

class Category(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '<Category %d>' % self.id

在上述程式碼中,Product模型新增了category_idcategory欄位。category_idCategory模型的外部索引鍵,而category代表了關聯表。

程式碼解析

category = db.relationship('Category', backref=db.backref('products', lazy='dynamic'))

此行程式碼建立了ProductCategory之間的關聯。backref引數允許我們從Category模型中存取產品。

修改檢視函式

接下來,修改views.py檔案以適應模型的變化。首先,修改products()方法:

from my_app.catalog.models import Product, Category

@catalog.route('/products')
def products():
    products = Product.query.all()
    res = {}
    for product in products:
        res[product.id] = {
            'name': product.name,
            'price': product.price,
            'category': product.category.name
        }
    return jsonify(res)

此處,我們在產品的JSON資料中增加了類別名稱。

建立類別與產品的關聯

修改create_product()方法以在建立產品之前查詢類別:

@catalog.route('/product-create', methods=['POST',])
def create_product():
    name = request.form.get('name')
    price = request.form.get('price')
    categ_name = request.form.get('category')
    category = Category.query.filter_by(name=categ_name).first()
    if not category:
        category = Category(categ_name)
    product = Product(name, price, category)
    db.session.add(product)
    db.session.commit()
    return 'Product created.'

新增類別建立方法

新增create_category()方法以處理類別的建立:

@catalog.route('/category-create', methods=['POST',])
def create_category():
    name = request.form.get('name')
    category = Category(name)
    db.session.add(category)
    db.session.commit()
    return 'Category created.'

取得所有類別及其產品

新增categories()方法以處理所有類別及其對應產品的列表:

@catalog.route('/categories')
def categories():
    categories = Category.query.all()
    res = {}
    for category in categories:
        res[category.id] = {
            'name': category.name
        }
        res[category.id]['products'] = []
        for product in category.products:
            res[category.id]['products'].append({
                'id': product.id,
                'name': product.name,
                'price': product.price
            })
    return jsonify(res)

使用Alembic和Flask-Migrate進行資料函式庫遷移

在實際應用中,更新資料函式庫架構是一個常見的需求。直接刪除資料函式庫並重新建立並不是一個好的做法,尤其是在生產環境中。Alembic是一個用於管理資料函式庫遷移的Python工具,它根據SQLAlchemy。Flask-Migrate是一個Flask擴充套件,使得遷移過程更加簡單。

安裝Flask-Migrate

首先,安裝Flask-Migrate:

$ pip install Flask-Migrate

組態Flask-Migrate

修改應用程式定義以啟用遷移:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)

使用Flask-Migrate進行遷移

使用以下命令初始化遷移倉函式庫:

$ flask db init

建立初始遷移指令碼:

$ flask db migrate

應用遷移:

$ flask db upgrade

透過上述步驟,我們可以在Flask應用程式中有效地管理和遷移資料函式庫架構。