Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Neste tutorial, você usará um sinalizador de recurso variante para gerenciar experiências destinadas a diferentes segmentos de usuários em um aplicativo de exemplo, Citação do Dia. Use o sinalizador de recurso de variantes criado em Como usar sinalizadores de recursos de variantes. Antes de continuar, certifique-se de criar o sinalizador de recurso variante chamado Greeting no repositório do App Configuration.
Pré-requisitos
- Python 3.8 ou posterior – para obter informações sobre como configurar o Python no Windows, confira a Documentação do Python no Windows
- Siga o tutorial Como usar sinalizadores de recursos de variantes e crie o sinalizador de recurso de variantes chamado Saudação.
Configurar um aplicativo Web Flask em Python
Se você já tiver um aplicativo Web Flask em Python, poderá pular para a seção Usar o sinalizador de recurso de variantes.
Crie uma 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 o aplicativo Quote of the Day
Crie um novo arquivo chamado app.py na pasta QuoteOfTheDay com o conteúdo a seguir. 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 arquivo chamado model.py na pasta QuoteOfTheDay com o seguinte conteúdo. Ele define uma classe de dados
Quotee 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 arquivo chamado routes.py na pasta QuoteOfTheDay com o seguinte conteúdo. Ele define rotas para o aplicativo Web Flask, manipulando a autenticação do usuário e exibindo uma home page com uma citaçã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 pasta chamada modelos na pasta QuoteOfTheDay e adicione um novo arquivo chamado base.html nela com o conteúdo a seguir. Ele define a página de layout do 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 arquivo chamado index.html na pasta modelos com o conteúdo a seguir. 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 arquivo chamado sign_up.html na pasta modelos com o conteúdo a seguir. Ele define o modelo da 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 arquivo chamado login.html na pasta modelos com o conteúdo a seguir. Ele define o modelo da página de logon 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 pasta chamada estático na pasta QuoteOfTheDay e adicione um novo arquivo chamado site.css nela com o conteúdo a seguir. 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 de grade
Instale as versões mais recentes dos seguintes pacotes.
pip install azure-identity pip install azure-appconfiguration-provider pip install featuremanagement[AzureMonitor]Abra o arquivo app.py . Adicione o código a seguir entre
bcrypt = Bcrypt(app)edb = SQLAlchemy(). Ele se conecta à Configuração de Aplicativos e configura o gerenciamento de recursos.Use a autenticação
DefaultAzureCredentialno repositório da Configuração de Aplicativos. Siga as instruções para atribuir à credencial a função Leitor de Dados de Configuração de Aplicativo. Certifique-se de permitir tempo suficiente para a permissão se propagar 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 código a seguir para que
greeting_messageobtenha a variante do recurso.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 do Azure, acesse o repositório de Configuração de Aplicativos criado anteriormente e adicione os seguintes valores de chave. Criar um valor-chave
| Chave | Valor | Description |
|---|---|---|
| "SQLALCHEMY_DATABASE_URI" | "sqlite:///db.sqlite" | Usado por Flask-SQLAlchemy para saber a qual banco de dados se conectar |
| "SECRET_KEY" | "fake-secret-key" | Usado pelo Flask para assinar cookies de sessão (proteção de integridade) para funcionalidade de autenticação |
Em app.py, a load(...) função recupera dados de configuração de Configuração de Aplicativos do Azure e os armazena em azure_app_config. A chamada app.config.update(azure_app_config) então aplica as definições de configuração recuperadas à aplicação Flask.
Compilar e executar o aplicativo
Defina a variável de ambiente chamada AzureAppConfigurationEndpoint como o ponto de extremidade do repositório da Configuração de Aplicativos, encontrado na Visão geral do seu repositório no portal do Azure.
Se você usar o prompt de comando do Windows, execute o comando a seguir 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 comando a seguir:
export AzureAppConfigurationEndpoint='<AppConfigurationEndpoint>'No prompt de comando, na pasta QuoteOfTheDay, execute:
flask run.Aguarde até que o aplicativo seja iniciado e, em seguida, abra um navegador e navegue até
http://localhost:5000/.Depois de exibir o aplicativo em execução, selecione Registrar na parte superior direita para registrar um novo usuário.
Registrar um novo usuário chamado usera@contoso.com.
Observação
É importante para a finalidade deste tutorial usar exatamente esses nomes. 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.
Seu login é feito automaticamente. Você deve ver que usera@contoso.com vê a mensagem longa ao visualizar o aplicativo.
Saia usando o botão Sair no canto superior direito.
Registrar um segundo usuário chamado userb@contoso.com.
Seu login é feito automaticamente. Você deve ver que userb@contoso.com vê a mensagem curta ao visualizar o aplicativo.
Próximas etapas
Para obter o resumo completo dos recursos da biblioteca de gerenciamento de recursos do Python, consulte o documento a seguir.