在本教學課程中,您會使用變體功能旗標來管理範例應用程式中不同使用者區段的體驗, 「日引述」。 您可以使用如何在變化功能旗標中 建立的 Variant 功能旗標。 繼續之前,請確定您在 應用程式組態 存放區中建立名為 Greeting 的變體功能旗標。
必要條件
- Python 3.8 或更新版本 - 如需在 Windows 上設定 Python 的詳細資訊,請參閱 Windows 上的 Python 文件
- 遵循如何變體功能旗標教學課程,並建立名為 Greeting 的變體功能旗標。
設定 Python Flask Web 應用程式
如果您已經有 Python Flask Web 應用程式,您可以跳至 使用 Variant 功能旗標 區段。
建立名為 QuoteOfTheDay 的新項目資料夾。
在 QuoteOfTheDay 資料夾中建立虛擬環境。
python -m venv venv
啟用虛擬環境。
.\venv\Scripts\Activate
安裝下列套件的最新版本。
pip install flask pip install flask-login pip install flask_sqlalchemy pip install flask_bcrypt
建立 Day 應用程式的報價
使用下列內容,在
QuoteOfTheDay
資料夾中建立名為app.py
的新檔案。 它會使用使用者驗證來設定基本的 Flask Web 應用程式。from flask_bcrypt import Bcrypt from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager from flask import Flask app = Flask(__name__, template_folder="../templates", static_folder="../static") bcrypt = Bcrypt(app) db = SQLAlchemy() db.init_app(app) login_manager = LoginManager() login_manager.init_app(app) from .model import Users @login_manager.user_loader def loader_user(user_id): return Users.query.get(user_id) with app.app_context(): db.create_all() if __name__ == "__main__": app.run(debug=True) from . import routes app.register_blueprint(routes.bp)
使用下列內容,在QuoteOfTheDay資料夾中建立名為 model.py 的新檔案。 它會定義
Quote
Flask Web 應用程式的數據類別和使用者模型。from dataclasses import dataclass from flask_login import UserMixin from . import db @dataclass class Quote: message: str author: str # Create user model class Users(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(250), unique=True, nullable=False) password_hash = db.Column(db.String(250), nullable=False) def __init__(self, username, password): self.username = username self.password_hash = password
在 QuoteOfTheDay 資料夾中建立名為 routes.py的新檔案,其中包含下列內容。 它會定義 Flask Web 應用程式的路由、處理使用者驗證,以及顯示具有隨機引號的首頁。
import random from flask import Blueprint, render_template, request, flash, redirect, url_for from flask_login import current_user, login_user, logout_user from . import db, bcrypt from .model import Quote, Users bp = Blueprint("pages", __name__) @bp.route("/", methods=["GET", "POST"]) def index(): context = {} user = "" if current_user.is_authenticated: user = current_user.username context["user"] = user else: context["user"] = "Guest" if request.method == "POST": return redirect(url_for("pages.index")) quotes = [ Quote("You cannot change what you are, only what you do.", "Philip Pullman"), ] greeting_message = "Hi" context["model"] = {} context["model"]["greeting_message"] = greeting_message context["model"]["quote"] = {} context["model"]["quote"] = random.choice(quotes) context["isAuthenticated"] = current_user.is_authenticated return render_template("index.html", **context) @bp.route("/register", methods=["GET", "POST"]) def register(): if request.method == "POST": password = request.form.get("password") hashed_password = bcrypt.generate_password_hash(password).decode('utf-8') user = Users(request.form.get("username"), hashed_password) try: db.session.add(user) db.session.commit() except Exception as e: flash("Username already exists") return redirect(url_for("pages.register")) login_user(user) return redirect(url_for("pages.index")) return render_template("sign_up.html") @bp.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": user = Users.query.filter_by(username=request.form.get("username")).first() password = request.form.get("password") if user and bcrypt.check_password_hash(user.password_hash, password): login_user(user) return redirect(url_for("pages.index")) return render_template("login.html") @bp.route("/logout") def logout(): logout_user() return redirect(url_for("pages.index"))
在 QuoteOfTheDay 資料夾中建立名為 templates 的新資料夾,並在其中新增名為 base.html 的新檔案,其中包含下列內容。 它會定義 Web 應用程式的版面配置頁面。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>QuoteOfTheDay</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> <link rel="stylesheet" href="{{ url_for('static', filename='site.css') }}"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"> </head> <body> <header> <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3"> <div class="container"> <a class="navbar-brand" href="/">QuoteOfTheDay</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between"> <ul class="navbar-nav flex-grow-1"> <li class="nav-item"> <a class="nav-link text-dark" href="/">Home</a> </li> </ul> {% block login_partial %} <ul class="navbar-nav"> {% if isAuthenticated %} <li class="nav-item"> <a class="nav-link text-dark">Hello {{user}}!</a> </li> <li class="nav-item"> <a class="nav-link text-dark" href="/logout">Logout</a> </li> {% else %} <li class="nav-item"> <a class="nav-link text-dark" href="/register">Register</a> </li> <li class="nav-item"> <a class="nav-link text-dark" href="/login">Login</a> </li> {% endif %} </ul> {% endblock %} </div> </div> </nav> </header> <div class="container"> <main role="main" class="pb-3"> {% block content %} {% endblock %} </main> </div> </body> <footer class="border-top footer text-muted"> <div class="container"> © 2024 - QuoteOfTheDay </div> </footer> <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> </body> </html>
使用下列內容,在templates資料夾中建立名為 index.html 的新檔案。 它會擴充基底範本,並新增內容區塊。
{% extends 'base.html' %} {% block content %} <div class="quote-container"> <div class="quote-content"> {% if model.greeting_message %} <h3 class="greeting-content">{{model.greeting_message}}</h3> {% endif %} <br /> <p class="quote">“{{model.quote.message}}”</p> <p>- <b>{{model.quote.author}}</b></p> </div> <div class="vote-container"> <button class="btn btn-primary" onclick="heartClicked(this)"> <i class="far fa-heart"></i> <!-- Heart icon --> </button> </div> <form action="/" method="post"> </form> </div> <script> function heartClicked(button) { var icon = button.querySelector('i'); icon.classList.toggle('far'); icon.classList.toggle('fas'); } </script> {% endblock %}
使用下列內容,在templates資料夾中建立名為 sign_up.html 的新檔案。 它會定義用戶註冊頁面的範本。
{% extends 'base.html' %} {% block content %} <div class="login-container"> <h1>Create an account</h1> <form action="#" method="post"> <label for="username">Username:</label> <input type="text" name="username" /> <label for="password">Password:</label> <input type="password" name="password" /> <button type="submit">Submit</button> </form> </div> {% endblock %}
使用下列內容,在templates資料夾中建立名為 login.html 的新檔案。 它會定義使用者登入頁面的範本。
{% extends 'base.html' %} {% block content %} <div class="login-container"> <h1>Login to your account</h1> <form action="#" method="post"> <label for="username">Username:</label> <input type="text" name="username" /> <label for="password">Password:</label> <input type="password" name="password" /> <button type="submit">Submit</button> </form> </div> {% endblock %}
在 QuoteOfTheDay 資料夾中建立名為 static 的新資料夾,並在其中新增名為 site.css的新檔案,其中包含下列內容。 它會新增 Web 應用程式的 CSS 樣式。
html { font-size: 14px; } @media (min-width: 768px) { html { font-size: 16px; } } .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; } html { position: relative; min-height: 100%; } body { margin-bottom: 60px; } body { font-family: Arial, sans-serif; background-color: #f4f4f4; color: #333; } .quote-container { background-color: #fff; margin: 2em auto; padding: 2em; border-radius: 8px; max-width: 750px; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); display: flex; justify-content: space-between; align-items: start; position: relative; } .login-container { background-color: #fff; margin: 2em auto; padding: 2em; border-radius: 8px; max-width: 750px; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); justify-content: space-between; align-items: start; position: relative; } .vote-container { position: absolute; top: 10px; right: 10px; display: flex; gap: 0em; } .vote-container .btn { background-color: #ffffff; /* White background */ border-color: #ffffff; /* Light blue border */ color: #333 } .vote-container .btn:focus { outline: none; box-shadow: none; } .vote-container .btn:hover { background-color: #F0F0F0; /* Light gray background */ } .greeting-content { font-family: 'Georgia', serif; /* More artistic font */ } .quote-content p.quote { font-size: 2em; /* Bigger font size */ font-family: 'Georgia', serif; /* More artistic font */ font-style: italic; /* Italic font */ color: #4EC2F7; /* Medium-light blue color */ }
使用 Variant 功能旗標
安裝下列套件的最新版本。
pip install azure-identity pip install azure-appconfiguration-provider pip install featuremanagement[AzureMonitor]
開啟 檔案,
app.py
並將下列程式代碼新增至檔案結尾。 它會連線到 應用程式組態 並設定功能管理。您可以使用
DefaultAzureCredential
來向 應用程式組態 存放區進行驗證。 請遵循指示來指派認證 應用程式組態 數據讀取者角色。 在執行應用程式之前,請務必允許足夠的時間來傳播許可權。import os from azure.appconfiguration.provider import load from featuremanagement import FeatureManager from azure.identity import DefaultAzureCredential ENDPOINT = os.getenv("AzureAppConfigurationEndpoint") # Updates the flask app configuration with the Azure App Configuration settings whenever a refresh happens def callback(): app.config.update(azure_app_config) # Connect to App Configuration global azure_app_config azure_app_config = load( endpoint=ENDPOINT, credential=DefaultAzureCredential(), on_refresh_success=callback, feature_flag_enabled=True, feature_flag_refresh_enabled=True, ) app.config.update(azure_app_config) # Create a FeatureManager feature_manager = FeatureManager(azure_app_config)
開啟
routes.py
並將下列程式代碼新增至其結尾,以重新整理組態並取得功能變體。from featuremanagement.azuremonitor import track_event from . import azure_app_config, feature_manager ... # Update the post request to track liked events if request.method == "POST": track_event("Liked", user) return redirect(url_for("pages.index")) ... # Update greeting_message to variant greeting = feature_manager.get_variant("Greeting", user) greeting_message = "" if greeting: greeting_message = greeting.configuration
建置並執行應用程式
將名為 AzureAppConfigurationEndpoint 的環境變數設定為您在 Azure 入口網站 中存放區概觀底下找到的 應用程式組態 存放區端點。
如果您使用 Windows 命令提示字元,請執行下列命令,然後重新啟動命令提示字元以讓變更生效:
setx AzureAppConfigurationEndpoint "<endpoint-of-your-app-configuration-store>"
如果您使用 PowerShell,請執行下列命令:
$Env:AzureAppConfigurationEndpoint = "<endpoint-of-your-app-configuration-store>"
如果您使用 macOS 或 Linux,請執行下列命令:
export AzureAppConfigurationEndpoint='<endpoint-of-your-app-configuration-store'
在命令提示字元中,於 QuoteOfTheDay 資料夾內執行:
flask run
。等候應用程式啟動,然後開啟瀏覽器並流覽至
http://localhost:5000/
。檢視執行中的應用程式之後,選取右上方的 [註冊] 以註冊新的使用者。
註冊名為 usera@contoso.com 的新使用者。
注意
請務必讓本教學課程正確使用這些名稱。 只要功能如預期般設定,這兩個使用者應該會看到不同的變體。
輸入使用者資訊之後,選取 [ 提交 ] 按鈕。
您會自動登入。 您應該會在檢視應用程式時看到 usera@contoso.com 長訊息。
使用右上方的 [註銷] 按鈕註銷 。
註冊名為 userb@contoso.com的第二個使用者。
您會自動登入。 您應該會在檢視應用程式時看到 userb@contoso.com 簡短訊息。
下一步
如需 Python 功能管理連結庫的完整功能取消,請參閱下列檔。