Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Neste tutorial, utiliza uma funcionalidade de variantes para gerir experiências para diferentes segmentos de utilizadores numa aplicação de exemplo, Citação do Dia. Utiliza a flag de funcionalidade variante criada em Como criar flags de funcionalidade variantes. Antes de continuar, certifique-se de criar o sinalizador de recurso variante chamado Saudação na sua loja de configuração de aplicativos.
Pré-requisitos
- Python 3.8 ou posterior - para obter informações sobre como configurar o Python no Windows, consulte a documentação do Python no Windows
- Siga o tutorial Como utilizar feature flags variantes e crie a feature flag variante chamada Greeting.
Configurar um aplicativo Web Python Flask
Se já tiver uma aplicação web Python Flask, pode saltar para a secção Utilizar o sinalizador de funcionalidade de variante.
Crie uma nova pasta de projeto chamada QuoteOfTheDay.
Crie um ambiente virtual na pasta QuoteOfTheDay .
python -m venv venvAtive o ambiente virtual.
.\venv\Scripts\ActivateInstale as versões mais recentes dos seguintes pacotes.
pip install flask pip install flask-login pip install flask_sqlalchemy pip install flask_bcrypt
Criar a aplicação Citação do Dia
Crie um novo ficheiro chamado app.py na pasta QuoteOfTheDay com o seguinte conteúdo. Ele configura um aplicativo web Flask básico com autenticação de usuário.
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)Crie um novo arquivo chamado model.py na pasta QuoteOfTheDay com o seguinte conteúdo. Ele define uma classe de
Quotedados e um modelo de usuário para o aplicativo web Flask.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 = passwordCrie um novo arquivo chamado routes.py na pasta QuoteOfTheDay com o seguinte conteúdo. Ele define rotas para a aplicação web Flask, manipulando a autenticação do usuário e exibindo uma página inicial com uma cotação aleatória.
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"))Crie uma nova pasta chamada templates na pasta QuoteOfTheDay e adicione um novo arquivo chamado base.html nela com o seguinte conteúdo. Ele define a página de layout para o aplicativo 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>Crie um novo arquivo chamado index.html na pasta de modelos com o seguinte conteúdo. Ele estende o modelo base e adiciona o bloco de conteúdo.
{% 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 %}Crie um novo arquivo chamado sign_up.html na pasta de modelos com o seguinte conteúdo. Ele define o modelo para a página de registro do usuário.
{% 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 %}Crie um novo arquivo chamado login.html na pasta de modelos com o seguinte conteúdo. Ele define o modelo para a página de login do usuário.
{% 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 %}Crie uma nova pasta chamada static na pasta QuoteOfTheDay e adicione um novo arquivo chamado site.css nela com o seguinte conteúdo. Ele adiciona estilos CSS para o aplicativo Web.
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 */ }
Usar o sinalizador de recurso variante
Instale as versões mais recentes dos seguintes pacotes.
pip install azure-identity pip install azure-appconfiguration-provider pip install featuremanagement[AzureMonitor]Abre o ficheiro app.py . Adicione o seguinte código entre
bcrypt = Bcrypt(app)edb = SQLAlchemy(). Ele se conecta à Configuração do aplicativo e configura o gerenciamento de recursos.Utiliza o
DefaultAzureCredentialpara se autenticar no seu arquivo de App Configuration. Siga as instruções para atribuir à sua credencial a função de Leitor de Dados de Configuração do Aplicativo. Certifique-se de dar tempo suficiente para que a permissão se propague antes de executar seu aplicativo.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)Abra routes.py e atualize o seguinte código para
greeting_messagede modo a obter a variante da funcionalidade.from app import feature_manager # Update greeting_message to variant greeting = feature_manager.get_variant("Greeting", user) greeting_message = "" if greeting: greeting_message = greeting.configurationNo portal Azure, vá à loja de Configuração de Aplicações que criou anteriormente e adicione os seguintes valores-chave. Crie uma chave-valor
| Chave | valor | Description |
|---|---|---|
| "SQLALCHEMY_DATABASE_URI" | "sqlite:///db.sqlite" | Usado por Flask-SQLAlchemy para saber a que base de dados se ligar |
| "SECRET_KEY" | "chave secreta falsa" | Usado pelo Flask para assinar cookies de sessão (proteção de integridade) para efeitos de autenticação |
Em app.py, a load(...) função recupera dados de configuração de Azure App Configuration e armazena-os em azure_app_config. A app.config.update(azure_app_config) chamada aplica então as definições de configuração recuperadas à aplicação Flask.
Compilar e executar a aplicação
Defina uma variável de ambiente chamada AzureAppConfigurationEndpoint para o ponto de extremidade do seu repositório de Configuração de Aplicativo encontrado em Visão geral da sua loja no portal do Azure.
Se você usar o prompt de comando do Windows, execute o seguinte comando e reinicie o prompt de comando para permitir que a alteração entre em vigor:
setx AzureAppConfigurationEndpoint "<AppConfigurationEndpoint>"Se você usar o PowerShell, execute o seguinte comando:
$Env:AzureAppConfigurationEndpoint = "<AppConfigurationEndpoint>"Se você usa macOS ou Linux, execute o seguinte comando:
export AzureAppConfigurationEndpoint='<AppConfigurationEndpoint>'Na linha de comandos, na pasta QuoteOfTheDay, execute o seguinte comando:
flask run.Aguarde até que a aplicação seja iniciada e, em seguida, abra um browser e navegue até
http://localhost:5000/.Depois de visualizar o aplicativo em execução, selecione Registrar no canto superior direito para registrar um novo usuário.
Registre um novo usuário chamado usera@contoso.com.
Nota
É importante para o propósito deste tutorial usar esses nomes exatamente. Desde que o recurso tenha sido configurado conforme o esperado, os dois usuários devem ver variantes diferentes.
Selecione o botão Enviar depois de inserir as informações do usuário.
Você está automaticamente conectado. Você deve ver que usera@contoso.com vê a mensagem longa ao visualizar o aplicativo.
Saia usando o botão Logout no canto superior direito.
Registre um segundo usuário chamado userb@contoso.com.
Você está automaticamente conectado. Você deve ver que userb@contoso.com vê a mensagem curta ao visualizar o aplicativo.
Próximos passos
Para obter o resumo completo dos recursos da biblioteca de gerenciamento de recursos do Python, consulte o documento a seguir.