Použití příznaků funkcí variant v aplikaci v Pythonu
V tomto kurzu použijete příznak variantní funkce ke správě prostředí pro různé uživatelské segmenty v ukázkové aplikaci Citát dne. Používáte příznak varianty vytvořený v části Postupy variantní příznaky funkcí. Než budete pokračovat, ujistěte se, že vytvoříte příznak funkce varianty s názvem Greeting (Pozdrav) v obchodě App Configuration Store.
Požadavky
- Python 3.8 nebo novější – informace o nastavení Pythonu ve Windows najdete v dokumentaci k Pythonu ve Windows.
- Postupujte podle kurzu Jak variantovat příznaky funkcí a vytvořte příznak variant funkce s názvem Greeting.
Nastavení webové aplikace Python Flask
Pokud už máte webovou aplikaci Python Flask, můžete přeskočit na část Použití příznaku funkce varianty.
Vytvořte novou složku projektu s názvem QuoteOfTheDay.
Vytvořte virtuální prostředí ve složce QuoteOfTheDay .
python -m venv venv
Aktivujte virtuální prostředí.
.\venv\Scripts\Activate
Nainstalujte nejnovější verze následujících balíčků.
pip install flask pip install flask-login pip install flask_sqlalchemy pip install flask_bcrypt
Vytvoření nabídky aplikace Den
Ve složce vytvořte nový soubor
app.py
QuoteOfTheDay
s následujícím obsahem. Nastaví základní webovou aplikaci Flask s ověřováním uživatelů.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)
Ve složce QuoteOfTheDay vytvořte nový soubor s názvem model.pys následujícím obsahem. Definuje datovou
Quote
třídu a uživatelský model pro webovou aplikaci Flask.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
Ve složce QuoteOfTheDay vytvořte nový soubor s názvem routes.pys následujícím obsahem. Definuje trasy pro webovou aplikaci Flask, zpracovává ověřování uživatelů a zobrazuje domovskou stránku s náhodným uvozovkou.
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"))
Ve složce QuoteOfTheDay vytvořte novou složku s názvem šablonya přidejte do ní nový soubor s názvem base.html s následujícím obsahem. Definuje stránku rozložení webové aplikace.
<!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>
Ve složce šablon vytvořte nový soubor s názvem index.html s následujícím obsahem. Rozšíří základní šablonu a přidá blok obsahu.
{% 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 %}
Ve složce šablon vytvořte nový soubor s názvem sign_up.htmls následujícím obsahem. Definuje šablonu pro stránku registrace uživatele.
{% 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 %}
Ve složce šablon vytvořte nový soubor s názvem login.htmls následujícím obsahem. Definuje šablonu pro přihlašovací stránku uživatele.
{% 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 %}
Ve složce QuoteOfTheDay vytvořte novou složku s názvem statickáa přidejte do ní nový soubor s názvem site.css s následujícím obsahem. Přidá styly CSS pro webovou aplikaci.
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 */ }
Použití příznaku funkce varianty
Nainstalujte nejnovější verze následujících balíčků.
pip install azure-identity pip install azure-appconfiguration-provider pip install featuremanagement[AzureMonitor]
app.py
Otevřete soubor a na konec souboru přidejte následující kód. Připojí se ke službě App Configuration a nastaví správu funkcí.Použijete
DefaultAzureCredential
k ověření ve službě App Configuration Store. Postupujte podle pokynů a přiřaďte své přihlašovací údaje roli Čtenář dat konfigurace aplikace. Před spuštěním aplikace nezapomeňte povolit dostatek času, než se oprávnění rozšíří.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)
Otevřete
routes.py
a na konec přidejte následující kód pro aktualizaci konfigurace a získejte variantu funkce.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
Sestavte a spusťte aplikaci.
Nastavte proměnnou prostředí s názvem AzureAppConfigurationEndpoint na koncový bod úložiště app Configuration, který najdete v části Přehled obchodu na webu Azure Portal.
Pokud používáte příkazový řádek systému Windows, spusťte následující příkaz a restartujte příkazový řádek, aby se změna projevila:
setx AzureAppConfigurationEndpoint "<endpoint-of-your-app-configuration-store>"
Pokud používáte PowerShell, spusťte následující příkaz:
$Env:AzureAppConfigurationEndpoint = "<endpoint-of-your-app-configuration-store>"
Pokud používáte macOS nebo Linux, spusťte následující příkaz:
export AzureAppConfigurationEndpoint='<endpoint-of-your-app-configuration-store'
Na příkazovém řádku ve složce QuoteOfTheDay spusťte:
flask run
.Počkejte, než se aplikace spustí, a pak otevřete prohlížeč a přejděte na
http://localhost:5000/
.Po zobrazení spuštěné aplikace vyberte Zaregistrovat v pravém horním rohu a zaregistrujte nového uživatele.
Zaregistrujte nového uživatele s názvem usera@contoso.com.
Poznámka:
Pro účely tohoto kurzu je důležité používat tyto názvy přesně. Pokud je funkce nakonfigurovaná podle očekávání, měli by dva uživatelé vidět různé varianty.
Po zadání informací o uživateli vyberte tlačítko Odeslat.
Jste automaticky přihlášení. Při prohlížení aplikace by se měla zobrazit usera@contoso.com dlouhá zpráva.
Odhlaste se pomocí tlačítka Odhlásit se v pravém horním rohu.
Zaregistrujte druhého uživatele s názvem userb@contoso.com.
Jste automaticky přihlášení. Při prohlížení aplikace by se měla zobrazit userb@contoso.com krátká zpráva.
Další kroky
Úplný běh funkcí knihovny pro správu funkcí Pythonu najdete v následujícím dokumentu.