Verwenden von Variantenfeatureflags in einer Python-Anwendung
In diesem Tutorial verwenden Sie Variantenfeatureflags zum Verwalten der Benutzeroberflächen für verschiedene Benutzersegmente in der Beispielanwendung Zitat des Tages. Sie verwenden das in Anleitung zur Verwendung von Variantenfeatureflags erstellte Variantenfeatureflag. Erstellen Sie vor dem Fortfahren unbedingt das Variantenfeatureflag mit dem Namen Greeting (Begrüßung) im App Configuration-Speicher.
Voraussetzungen
- Python 3.8 oder höher: Informationen zum Einrichten von Python unter Windows finden Sie in der Dokumentation zu Python unter Windows.
- Befolgen Sie das Tutorial Anleitung zur Verwendung von Variantenfeatureflags, und erstellen Sie das Variantenfeatureflag namens Gruß.
Einrichten einer Python Flask-Webanwendung
Wenn Sie bereits über eine Python Flask-Webanwendung verfügen, können Sie zum Abschnitt Verwenden des Variantenfeatureflags überspringen.
Erstellen Sie einen neuen Projektordner namens QuoteOfTheDay.
Erstellen Sie eine virtuelle Umgebung im Ordner QuoteOfTheDay.
python -m venv venv
Aktivieren Sie die virtuelle Umgebung.
.\venv\Scripts\Activate
Installieren Sie die neuesten Versionen der folgenden Pakete.
pip install flask pip install flask-login pip install flask_sqlalchemy pip install flask_bcrypt
Erstellen der App „Zitats der Tages“
Erstellen Sie eine neue Datei namens
app.py
mit folgendem Inhalt imQuoteOfTheDay
-Ordner. Sie richtet eine einfache Flask-Webanwendung mit Benutzerauthentifizierung ein.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)
Erstellen Sie eine neue Datei namens model.py mit folgendem Inhalt im Ordner QuoteOfTheDay. Sie definiert eine
Quote
-Datenklasse und ein Benutzermodell für die Flask-Webanwendung.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
Erstellen Sie eine neue Datei namens routes.py mit folgendem Inhalt im Ordner QuoteOfTheDay. Sie legt die Routen für die Flask-Webanwendung fest, übernimmt die Benutzerauthentifizierung und zeigt eine Homepage mit einem zufälligen Zitat an.
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"))
Erstellen Sie einen neuen Ordner namens Vorlagen im Ordner QuoteOfTheDay, und fügen Sie darin eine neue Datei namens base.html mit dem folgenden Inhalt hinzu. Sie definiert die Layoutseite für die Webanwendung.
<!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>
Erstellen Sie eine neue Datei namens index.html im Ordner Vorlagen mit dem folgenden Inhalt. Sie erweitert die Basisvorlage und fügt den Inhaltsblock hinzu.
{% 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 %}
Erstellen Sie eine neue Datei namens sign_up.html im Ordner Vorlagen mit dem folgenden Inhalt. Sie definiert die Vorlage für die Benutzerregistrierungsseite.
{% 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 %}
Erstellen Sie eine neue Datei namens login.html im Ordner Vorlagen mit dem folgenden Inhalt. Sie definiert die Vorlage für die Benutzeranmeldungsseite.
{% 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 %}
Erstellen Sie einen neuen Ordner namens Statisch im Ordner QuoteOfTheDay, und fügen Sie darin eine neue Datei namens site.css mit dem folgenden Inhalt hinzu. Sie fügt CSS-Formatvorlagen für die Webanwendung hinzu.
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 */ }
Verwenden der von Variantenfeatureflags
Installieren Sie die neuesten Versionen der folgenden Pakete.
pip install azure-identity pip install azure-appconfiguration-provider pip install featuremanagement[AzureMonitor]
Öffnen Sie die Datei
app.py
und fügen Sie am Ende der Datei den folgenden Code hinzu. Sie stellt eine Verbindung mit Azure App Configuration und richtet die Featureverwaltung ein.Sie verwenden die
DefaultAzureCredential
für die Authentifizierung beim App Configuration-Speicher. Befolgen Sie die Anweisungen, um Ihre Anmeldeinformationen der Rolle App Configuration-Datenleser zuzuweisen. Achten Sie darauf, ausreichend Zeit für die Verteilung der Berechtigung zu warten, bevor Sie Ihre Anwendung ausführen.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)
Öffnen Sie
routes.py
und fügen Sie den folgenden Code am Ende hinzu, um die Konfiguration zu aktualisieren und die Featurevariante abzurufen.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
Erstellen und Ausführen der App
Legen Sie eine Umgebungsvariable namens AzureAppConfigurationEndpoint auf den Endpunkt Ihres App Configuration-Speichers fest, den Sie in der Übersicht Ihres Speichers im Azure-Portal finden.
Führen Sie bei Verwendung einer Windows-Eingabeaufforderung den folgenden Befehl aus, und starten Sie die Eingabeaufforderung neu, damit die Änderung wirksam wird:
setx AzureAppConfigurationEndpoint "<endpoint-of-your-app-configuration-store>"
Wenn Sie PowerShell verwenden, und führen Sie den folgenden Befehl aus:
$Env:AzureAppConfigurationEndpoint = "<endpoint-of-your-app-configuration-store>"
Führen Sie bei Verwendung von macOS oder Linux den folgenden Befehl aus:
export AzureAppConfigurationEndpoint='<endpoint-of-your-app-configuration-store'
Führen Sie in der Eingabeaufforderung im Ordner QuoteOfTheDay Folgendes aus:
flask run
.Warten Sie, bis die App gestartet wird, öffnen Sie dann einen Browser, und navigieren Sie zu
http://localhost:5000/
.Wählen Sie nach dem Anzeigen der ausgeführten Anwendung Registrieren oben rechts aus, um einen neuen Benutzer zu registrieren.
Registriert einen neuen Benutzer mit dem Namen usera@contoso.com.
Hinweis
Es ist wichtig, dass in diesem Tutorial genau diese Namen verwendet werden. Solange das Feature wie erwartet konfiguriert wurde, sollten die beiden Benutzer unterschiedliche Varianten sehen.
Wählen Sie die Schaltfläche Absenden aus, nachdem Sie die Benutzerinformationen eingegeben haben.
Sie werden automatisch angemeldet. Sie sollten sehen, dass für usera@contoso.com beim Anzeigen der App eine ausführliche Nachricht angezeigt wird.
Melden Sie sich über die Schaltfläche Abmelden oben rechts ab.
Registriert eine zweite benutzende Person mit dem Namen userb@contoso.com.
Sie werden automatisch angemeldet. Sie sollten sehen, dass für userb@contoso.com beim Anzeigen der App eine kurze Nachricht angezeigt wird.
Nächste Schritte
Eine vollständige Übersicht über die Python-Featureverwaltungsbibliothek finden Sie im folgenden Dokument.