Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In deze zelfstudie gebruikt u een vlag voor variantfuncties voor het beheren van ervaringen voor verschillende gebruikerssegmenten in een voorbeeldtoepassing, Quote of the Day. U gebruikt de variant-featureflag die u hebt gemaakt in How to variant feature flags. Voordat u doorgaat, moet u ervoor zorgen dat u de variantfeatureflag met de naam Greeting maakt in uw App Configuration-opslag.
Vereisten
- Python 3.8 of hoger: zie de Documentatie voor Python in Windows voor meer informatie over het instellen van Python in Windows
- Volg de zelfstudie Over variantfunctievlagmen en maak de vlag voor de variantfunctie met de naam Greeting.
Een Python Flask-web-app instellen
Als u al een Python Flask-web-app hebt, kunt u naar de sectie De variant-featureflag gebruiken gaan.
Maak een nieuwe projectmap met de naam QuoteOfTheDay.
Maak een virtuele omgeving in de map QuoteOfTheDay.
python -m venv venvActiveer de virtuele omgeving.
.\venv\Scripts\ActivateInstalleer de nieuwste versies van de volgende pakketten.
pip install flask pip install flask-login pip install flask_sqlalchemy pip install flask_bcrypt
Maak de Citaat van de Dag-app
Maak een nieuw bestand met de naam app.py in de map QuoteOfTheDay met de volgende inhoud. Hiermee stelt u een eenvoudige Flask-webtoepassing in met gebruikersverificatie.
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) import routes app.register_blueprint(routes.bp)Maak in de map QuoteOfTheDay een nieuw bestand met de naam model.py met de volgende inhoud. Hiermee definieert u een
Quotegegevensklasse en een gebruikersmodel voor de Flask-webtoepassing.from dataclasses import dataclass from flask_login import UserMixin from app 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 = passwordMaak in de map QuoteOfTheDay een nieuw bestand met de naam routes.py met de volgende inhoud. Het definieert routes voor de Flask-webtoepassing, verwerkt gebruikersauthenticatie en toont een startpagina met een willekeurig citaat.
import random from flask import Blueprint, render_template, request, flash, redirect, url_for from flask_login import current_user, login_user, logout_user from app import db, bcrypt from model import Quote, Users bp = Blueprint("pages", __name__) @bp.route("/", methods=["GET"]) def index(): context = {} user = "" if current_user.is_authenticated: user = current_user.username context["user"] = user else: context["user"] = "Guest" 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"))Maak een nieuwe map met de naam sjablonen in de map QuoteOfTheDay en voeg een nieuw bestand met de naam base.html toe met de volgende inhoud. Hiermee definieert u de indelingspagina voor de webtoepassing.
<!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>Maak een nieuw bestand met de naam index.html in de map sjablonen met de volgende inhoud. Het breidt de basissjabloon uit en voegt het inhoudsblok toe.
{% 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 %}Maak een nieuw bestand met de naam sign_up.html in de map sjablonen met de volgende inhoud. Hiermee definieert u de sjabloon voor de gebruikersregistratiepagina.
{% 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 %}Maak een nieuw bestand met de naam login.html in de map sjablonen met de volgende inhoud. Hiermee definieert u de sjabloon voor de aanmeldingspagina van de gebruiker.
{% 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 %}Maak een nieuwe map met de naam Statisch in de map QuoteOfTheDay en voeg een nieuw bestand met de naam site.css toe met de volgende inhoud. Hiermee worden CSS-stijlen toegevoegd voor de webtoepassing.
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 */ }
De vlag voor variantfuncties gebruiken
Installeer de nieuwste versies van de volgende pakketten.
pip install azure-identity pip install azure-appconfiguration-provider pip install featuremanagement[AzureMonitor]Open het bestand app.py . Voeg de volgende code toe tussen
bcrypt = Bcrypt(app)endb = SQLAlchemy(). Het maakt verbinding met App Configuration en stelt functiebeheer in.U gebruikt de
DefaultAzureCredentialom u aan te melden bij uw App Configuration-store. Volg de instructies om uw referentiegegeven de rol App Configuration Data Reader toe te wijzen. Zorg ervoor dat u voldoende tijd hebt om de machtiging door te geven voordat u de toepassing uitvoert.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)Open routes.py en pas de volgende code voor
greeting_messageaan om de featurevariant op te halen.from app import feature_manager # Update greeting_message to variant greeting = feature_manager.get_variant("Greeting", user) greeting_message = "" if greeting: greeting_message = greeting.configurationGa in de Azure-portal naar het App Configuration-archief dat u eerder hebt gemaakt en voeg de volgende sleutelwaarden toe. Een sleutelwaarde maken
| Sleutel | Value | Description |
|---|---|---|
| "SQLALCHEMY_DATABASE_URI" | "sqlite:///db.sqlite" | Wordt gebruikt door Flask-SQLAlchemy om te weten met welke database verbinding moet worden gemaakt |
| "SECRET_KEY" | fake-secret-key | Wordt door Flask gebruikt voor het ondertekenen van sessiecookies (integriteitsbeveiliging) voor verificatiefunctionaliteit |
In app.py haalt de load(...) functie configuratiegegevens op uit Azure App Configuration en slaat deze op in azure_app_config. De app.config.update(azure_app_config) aanroep past vervolgens de opgehaalde configuratie-instellingen toe op de Flask-toepassing.
Ontwikkel de app en voer deze uit
Stel een omgevingsvariabele met de naam AzureAppConfigurationEndpoint in op het eindpunt van uw App Configuration-store dat u vindt onder Overzicht van uw store in de Azure-portal.
Als u de Windows-opdrachtprompt gebruikt, voert u de volgende opdracht uit en start u de opdrachtprompt opnieuw om de wijziging door te voeren:
setx AzureAppConfigurationEndpoint "<AppConfigurationEndpoint>"Als u PowerShell gebruikt, voert u de volgende opdracht uit:
$Env:AzureAppConfigurationEndpoint = "<AppConfigurationEndpoint>"Als u macOS of Linux gebruikt, voert u de volgende opdracht uit:
export AzureAppConfigurationEndpoint='<AppConfigurationEndpoint>'Voer in de opdrachtprompt, in de map QuoteOfTheDay, het volgende uit:
flask runWacht tot de app is gestart en open vervolgens een browser en navigeer naar
http://localhost:5000/.Nadat u de actieve toepassing hebt bekeken, selecteert u Registreren in de rechterbovenhoek om een nieuwe gebruiker te registreren.
Registreer een nieuwe gebruiker met de naam usera@contoso.com.
Notitie
Het is belangrijk voor deze zelfstudie om deze namen precies te gebruiken. Zolang de functie is geconfigureerd zoals verwacht, moeten de twee gebruikers verschillende varianten zien.
Selecteer de knop Verzenden nadat u gebruikersgegevens hebt ingevoerd.
U wordt automatisch aangemeld. U zou moeten zien dat usera@contoso.com het lange bericht ziet wanneer u de app bekijkt.
Afmelden met de knop Afmelden in de rechterbovenhoek.
Registreer een tweede gebruiker met de naam userb@contoso.com.
U wordt automatisch aangemeld. U zou moeten zien dat userb@contoso.com het korte bericht ziet wanneer u de app bekijkt.
Volgende stappen
Raadpleeg het volgende document voor de volledige functierundown van de Python-bibliotheek voor functiebeheer.