Partilhar via


Instruções: Aplicação Web da API de Anúncios do Bing em Python

Este tutorial explora como começar a executar uma aplicação Web do Microsoft Advertising com o SDK Python de Anúncios do Bing, o IDE do Visual Studio Code e a arquitetura Web django .

Este tutorial não explora vários detalhes sobre o próprio Django, como trabalhar com modelos de dados e criar uma interface administrativa. Para obter orientações sobre esses aspetos, veja a documentação do Django. Para obter mais detalhes sobre como trabalhar com o Django no terminal, editor e depurador do VS Code, veja Utilizar o Django no Visual Studio Code. Este tutorial pede muito emprestado das instruções de configuração em Utilizar o Django no Visual Studio Code.

Descrição Geral da Aplicação Web de Exemplo

No final deste tutorial, terá uma aplicação Web em http://localhost execução que irá autenticar as suas credenciais de utilizador do Microsoft Advertising e apresentar as suas informações de utilizador e contas. Em seguida, pode adicionar vários utilizadores de aplicações Web, que podem ativar o acesso à sua aplicação para utilizarem as respetivas credenciais do Microsoft Advertising. Esta aplicação Web fornece um mapeamento de um para um utilizador de uma aplicação Web, por exemplo, ContosoUser para um utilizador do Microsoft Advertising. Para obter informações sobre como modificar o modelo de dados, consulte a documentação do Django para obter mais informações. Se o utilizador da aplicação Web permitir o acesso às respetivas contas Microsoft Advertising com uma conta Microsoft, é armazenado um token de atualização na base de dados SQL Lite no seu servidor Web.

Pré-requisitos

Terá de ter o Visual Studio Code instalado para seguir este tutorial. Para executar a aplicação Web Django, pode utilizar o Visual Studio Community ou o Visual Studio Professional. No entanto, os passos de configuração variam consoante os deste tutorial.

Terá de ter o Python 3 instalado a partir de python.org; Normalmente, utilize o botão Transferir Python 3.7.0 que aparece primeiro na página (ou seja qual for a versão mais recente). No Windows, certifique-se de que a localização do interpretador Python está incluída na variável de ambiente PATH. Pode verificar isto ao executar path na linha de comandos. Se a pasta do interpretador Python não estiver incluída, abra Definições do Windows, procure "ambiente", selecione Editar variáveis de ambiente para a sua conta e, em seguida, edite a variável Caminho para incluir essa pasta.

Terá de instalar o SDK Python do Bing Ads e este tutorial irá guiá-lo pela instalação.

Precisará da arquitetura Web django instalada para implementar a aplicação localmente e este tutorial irá guiá-lo pela instalação.

Precisará de, pelo menos, um utilizador com credenciais do Microsoft Advertising e um token de programador.

Terá de registar uma aplicação e tomar nota do ID de cliente (ID da aplicação registada) e do segredo do cliente (palavra-passe registada). Terá de registar uma aplicação Web (não nativa) para este exemplo. Ser-lhe-á pedido para registar um ou mais URLs de redirecionamento e, para este tutorial, deverá registar http://localhost/callback. Em vez disso, deve utilizar https quando implementado num servidor de produção. Para obter mais detalhes sobre o registo de uma aplicação e o fluxo de concessão de código de autorização, veja Authentication with OAuth (Autenticação com OAuth).

Este tutorial foi desenvolvido no Windows. Embora o Windows não seja necessário para executar o exemplo, alguns dos passos abaixo irão variar se utilizar outro sistema operativo, por exemplo, Linux ou MacOS.

Criar um ambiente de projeto para o Django

Nesta secção, vai criar um ambiente virtual no qual o Django está instalado. A utilização de um ambiente virtual evita a instalação do Django num ambiente Python global e dá-lhe controlo exato sobre as bibliotecas utilizadas numa aplicação.

  1. No seu sistema de ficheiros, crie uma pasta de projeto para este tutorial, como hello_django.

  2. Na pasta, abra o Powershell ou a sua shell de scripts favorita e utilize o seguinte comando para criar um ambiente virtual com o hello_django nome env com base no seu interpretador atual:

    py -3 -m venv env
    
  3. Abra a pasta do hello_django projeto no VS Code ao executar code .ou ao executar o VS Code e ao utilizar o comandoAbrir Pasta de Ficheiros>.

    Abrir o VS Code

  4. No VS Code, abra a Paleta de Comandos (Ver>Paleta de Comandos ou Ctrl+Shift+P). Em seguida, selecione o comando Python: Select Interpreter (Python: Selecionar Interpretador ).

  5. O comando apresenta uma lista de intérpretes disponíveis que o VS Code pode localizar automaticamente. A sua lista irá variar; Se não vir o interpretador pretendido, veja Configurar ambientes Python. Na lista, selecione o ambiente virtual na pasta do projeto que começa com ./env ou .\env:

    Selecionar o ambiente virtual para Python

  6. Executar Terminal: Novo Terminal (Ctrl+Shift+ ` ) a partir da Paleta de Comandos, que cria um terminal e ativa automaticamente o ambiente virtual ao executar o script de ativação.

    Nota

    No Windows, se o tipo de terminal predefinido for PowerShell, poderá ver um erro a indicar que não pode ser executado activate.ps1 porque a execução de scripts está desativada no sistema. O erro deve fornecer uma ligação para obter informações sobre como permitir scripts. Caso contrário, utilize Terminal: selecione Shell Predefinida para definir a predefinição preferida.

    O ambiente selecionado é apresentado no canto inferior esquerdo da barra de estado do VS Code. Repare no indicador (venv) que indica que está a utilizar um ambiente virtual:

    Ambiente selecionado apresentado na barra de estado do VS Code

  7. Instale o Django no ambiente virtual através do pip no Terminal do VS Code:

    python -m pip install django
    
  8. Instale o SDK Python do Bing Ads no ambiente virtual através do pip no Terminal do VS Code:

    python -m pip install bingads
    

Tem agora um ambiente virtual autónomo pronto para escrever código django e Microsoft Advertising.

Criar e executar uma aplicação Django

Na terminologia do Django, um "projeto Django" é composto por vários ficheiros de configuração ao nível do site, juntamente com uma ou mais "aplicações" que implementa num anfitrião Web para criar uma aplicação Web completa. Um projeto Django pode conter várias aplicações, cada uma das quais normalmente tem uma função independente no projeto e a mesma aplicação pode estar em vários projetos django. Uma aplicação, por seu lado, é apenas um pacote Python que segue determinadas convenções esperadas pelo Django.

Para criar uma aplicação Django, é necessário criar primeiro o projeto Django para servir de contentor para a aplicação e, em seguida, criar a própria aplicação. Para ambos os fins, utilize o utilitário administrativo Django, django-admin, que é instalado quando instala o pacote Django.

Criar o projeto Django

  1. No Terminal do VS Code onde o ambiente virtual está ativado, execute o seguinte comando:

    django-admin startproject web_project .
    

    Este startproject comando pressupõe (por utilização do . no final) que a pasta atual é a pasta do projeto e cria o seguinte:

    • manage.py: o utilitário administrativo da linha de comandos django para o projeto. Executa comandos administrativos para o projeto com python manage.py <command> [options].

    • Uma subpasta com o nome web_project, que contém os seguintes ficheiros:

      • __init__.py: um ficheiro vazio que indica ao Python que esta pasta é um pacote Python.
      • wsgi.py: um ponto de entrada para os servidores Web compatíveis com WSGI servirem o seu projeto. Normalmente, deixa este ficheiro tal como está, uma vez que fornece os hooks para servidores Web de produção.
      • settings.py: contém definições para o projeto Django, que modifica no decurso do desenvolvimento de uma aplicação Web.
      • urls.py: contém um índice para o projeto Django, que também modifica no decurso do desenvolvimento.

      Projeto Web django

  2. Para verificar o projeto Django, certifique-se de que o ambiente virtual está ativado e, em seguida, inicie o servidor de desenvolvimento do Django com o comando python manage.py runserver. O servidor é executado na porta predefinida 8000 e verá um resultado semelhante ao seguinte na janela de saída do terminal do VS Code:

    Performing system checks...
    
    System check identified no issues (0 silenced).
    
    You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
    Run 'python manage.py migrate' to apply them.
    October 18, 2018 - 05:38:23
    Django version 2.1.2, using settings 'web_project.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CTRL-BREAK.
    

    Quando executa o servidor pela primeira vez, este cria uma base de dados SQLite predefinida no ficheiro db.sqlite3, que geralmente se destina a fins de desenvolvimento, mas pode ser utilizada em produção para aplicações Web de baixo volume. Além disso, o servidor Web incorporado do Django destina-se apenas a fins de desenvolvimento local. No entanto, quando implementa num anfitrião Web, o Django utiliza o servidor Web do anfitrião. O wsgi.py módulo no projeto Django trata da ligação aos servidores de produção.

    Se quiser utilizar uma porta diferente da predefinição 8000, basta especificar o número da porta na linha de comandos, como python manage.py runserver 5000.

  3. Ctrl+click o http://127.0.0.1:8000/ URL na janela de saída do terminal do VS Code para abrir o browser predefinido para esse endereço. Se o Django estiver instalado corretamente e o projeto for válido, verá a página predefinida apresentada abaixo. A janela Saída do VS Code também mostra o registo do servidor.

    Vista predefinida do projeto Django vazio

  4. Quando terminar, feche a janela do browser e pare o servidor no VS Code, utilizando Ctrl+C conforme indicado na janela de saída do terminal do VS Code.

Criar uma aplicação Django para o Microsoft Advertising

  1. No Terminal do VS Code com o ambiente virtual ativado, execute o comando do utilitário administrativo na pasta do startapp projeto (onde manage.py reside):

    python manage.py startapp app
    

    O comando cria uma pasta chamada app que contém vários ficheiros de código e uma subpasta. Destes, trabalha frequentemente com views.py (que contém as funções que definem páginas na sua aplicação Web) e models.py (que contém classes que definem os objetos de dados). A migrations pasta é utilizada pelo utilitário administrativo do Django para gerir as versões da base de dados, conforme abordado mais adiante neste tutorial. Existem também os ficheiros apps.py (configuração da aplicação), admin.py (para criar uma interface administrativa) e tests.py (para testes de unidades), que não são abrangidos aqui.

  2. Em app/settings.py adicione o seguinte código e defina os seus próprios valores CLIENT_ID, CLIENT_SECRET, DEVELOPER_TOKEN e AMBIENTE .

    """
    Bing Ads API settings
    Edit with your credentials.
    """
    
    REDIRECTION_URI = "http://localhost:8000/callback"
    CLIENT_ID = "ClientIdGoesHere" # Your registered App ID
    CLIENT_SECRET="ClientSecretGoesHere" # Your registered App Password
    DEVELOPER_TOKEN = "DeveloperTokenGoesHere" # Your production developer token
    ENVIRONMENT = 'production'
    API_VERSION=13
    
  3. app Adicione app/settings.py à lista de aplicações instaladas.

        INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app',
    ]
    
  4. No Terminal do VS Code, crie as app/static/app pastas e app/templates/app :

    (env) PS C:\dev\hello_django> mkdir app/static/app
    (env) PS C:\dev\hello_django> mkdir app/templates/app 
    
  5. Nas pastas, crie um novo ficheiro com o app/static/app nome site.css e adicione os seguintes conteúdos.

    .message {
        font-weight: 600;
        color: blue;
    }
    
    .message_list th,td {
        text-align: left;
        padding-right: 15px;
    }
    
    .navbar {
        background-color: lightslategray;
        font-size: 1em;
        font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
        color: white;
        padding: 8px 5px 8px 5px;
    }
    
    .navbar a {
        text-decoration: none;
        color: inherit;
    }
    
    .navbar-brand {
        font-size: 1.2em;
        font-weight: 600;
    }
    
    .navbar-item {
        font-variant: small-caps;
        margin-left: 30px;
    }
    
    .body-content {
        padding: 5px;
        font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }
    
    input[name=message] {
        width: 80%;
    }
    
  6. app/templates/app Na pasta, crie um ficheiro, index.html com os conteúdos abaixo.

    {% extends "app/layout.html" %}
    {% block content %}
    {% if errors %}
    <div class="jumbotron">
        <section id="errors">
            <h1>Errors occurred in your last request to Bing Ads API.</h1>
            <table class="message_list">
                <tr>
                    <th>Code</th>
                    <th>ErrorCode</th>
                    <th>Message</th>
                </tr>
                {% for error in errors %}
                <tr>
                    <td>{{ error.Code }}</td> 
                    <td>{{ error.ErrorCode }}</td> 
                    <td>{{ error.Message }}</td> 
                </tr>
                {% endfor %}
            </table> 
        </section>
    </div>
    {% endif %}
    {% if user.is_authenticated  %}
    {% if bingadsuser  %}
    <div class="jumbotron">
        <section id="enabled">
            <h1>Your credentials have access to Microsoft Advertising.</h1>
            <table class="message_list">
                <tr>
                    <th>Id</th>
                    <th>UserName</th>
                    <th>First Name</th>
                    <th>Last Name</th>
                </tr>
                <tr>
                    <td>{{ bingadsuser.Id }}</td> 
                    <td>{{ bingadsuser.UserName }}</td> 
                    <td>{{ bingadsuser.Name.FirstName }}</td> 
                    <td>{{ bingadsuser.Name.LastName }}</td> 
                </tr>
            </table>  
        </section>
    </div>
    <div class="jumbotron">
        <section id="revoke">
            <p class="lead">Click here to revoke access for this app to your Microsoft Advertising accounts. You will then be able to login with a different Microsoft Advertising user. </p>
            <form id="revokeForm" action="/revoke" method="post" class="navbar-left">
                {% csrf_token %}
                <p><a href="javascript:document.getElementById('revokeForm').submit()" class="btn btn-primary btn-large">Delete Refresh Token</a></p>
            </form>
        </section>
    </div>
    <div class="jumbotron">
        <section id="accounts">        
            <h1>Account Details</h1>
            <table class="message_list">
                <thead>
                <tr>
                    <th>Id</th>
                    <th>Name</th> 
                </tr>
                </thead>
                <tbody>
                {% for account in accounts %}
                <tr>
                    <td>{{ account.Id }}</td>
                    <td>{{ account.Name }}</td> 
                </tr>
                {% endfor %}
                </tbody>
            </table> 
        </section>
    </div>
    {% else  %}
    <div class="jumbotron">
        <section id="enable">
            <h1>Enable Microsoft Advertising Access</h1>
            <p class="lead">
                You are logged into the Django web application, but not yet signed in with your Microsoft Advertising credentials. 
                You can sign in with Microsoft Advertising credentials below.
            </p>
        </section>
    </div>
    <div>
        <div class="col-md-6">
            <section id="socialLoginForm">
                <h1>Microsoft Account Login</h1>
                <p class="lead">
                    Click here to authenticate your Microsoft Account. 
                    If you don't have Microsoft Advertising credentials, you can go to the 
                    <a href="https://ads.microsoft.com/customer/Signup.aspx">Microsoft Advertising Sign Up</a> page.
                </p>
                <p><a href="/callback" class="btn btn-primary btn-large">Authenticate Microsoft Account &raquo;</a></p>
            </section>
        </div>    
    </div>
    {% endif %}
    {% else %}
    <div class="jumbotron">
        <div class="col-md-6">
            <section id="socialLoginForm">
                <h1>Microsoft Advertising Example Web Application</h1>
                <p class="lead">
                    Before you can provide your Microsoft Advertising user credentials and access Microsoft Advertising data, 
                    you must <a href="{% url 'login' %}">login</a> to the Django web application.
                </p>
                <p class="lead">Use your site's Django admin portal to add web app users.</p>
                <p><a href="/admin" class="btn btn-primary btn-large">Django Admin &raquo;</a></p>
            </section>
        </div>    
    </div>
    {% endif %}
    <div>
        <div class="col-md-4">
            <h2>Get Started Using Python with Bing Ads API</h2>
            <p>The Bing Ads Python Software Development Kit (SDK) simplifies workflows such as OAuth authentication and report file parsing.</p>
            <p><a class="btn btn-default" href="https://learn.microsoft.com/advertising/guides/get-started-python">Learn more &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Django</h2>
            <p>Django is a free web framework for building Web sites and Web applications using HTML, CSS and JavaScript.</p>
            <p><a class="btn btn-default" href="https://www.djangoproject.com/">Learn more &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Microsoft Azure</h2>
            <p>You can publish your web app to Microsoft Azure. Find out how you can host your application with a free trial today.</p>
            <p><a class="btn btn-default" href="https://azure.microsoft.com">Learn more &raquo;</a></p>
        </div>
    </div>
    {% endblock %}
    {% block scripts %}
    {% load static %}
    <link rel="stylesheet" type="text/css" href="{% static 'app/site.css' %}"/>
    {% endblock %}
    
  7. app/templates/app Na pasta, crie um ficheiro, layout.html com os conteúdos abaixo.

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{{ title }} - My Django Application</title>
        {% load static %}
        <link rel="stylesheet" type="text/css" href="{% static 'app/site.css' %}"/>
        <script src="{% static 'app/scripts/modernizr-2.6.2.js' %}"></script>
    </head>
    <body>
        <div class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a href="/" class="navbar-brand">Microsoft Advertising App via Django</a>
                </div>
                <div class="navbar-collapse collapse">
                    <ul class="nav navbar-nav">
                        <li><a href="{% url 'home' %}">Home</a></li>
                    </ul>
                    {% include 'app/loginpartial.html' %}
                </div>
            </div>
        </div>
        <div class="container body-content">
    {% block content %}{% endblock %}
            <hr/>
            <footer>
                <p>&copy; {{ year }} - My Django Application</p>
            </footer>
        </div>
    {% block scripts %}{% endblock %}
    </body>
    </html>
    
  8. app/templates/app Na pasta, crie um ficheiro, login.html com os conteúdos abaixo.

    {% extends "app/layout.html" %}
    {% block content %}
    <h2>{{ title }}</h2>
    <div class="row">
        <div class="col-md-8">
            <section id="loginForm">
                <form action="." method="post" class="form-horizontal">
                    {% csrf_token %}
                    <h4>Use a local account to log in.</h4>
                    <hr />
                    <div class="form-group">
                        <label for="id_username" class="col-md-2 control-label">User name</label>
                        <div class="col-md-10">
                            {{ form.username }}
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="id_password" class="col-md-2 control-label">Password</label>
                        <div class="col-md-10">
                            {{ form.password }}
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-md-offset-2 col-md-10">
                            <input type="hidden" name="next" value="/" />
                            <input type="submit" value="Log in" class="btn btn-default" />
                        </div>
                    </div>
                    {% if form.errors %}
                    <p class="validation-summary-errors">Please enter a correct user name and password.</p>
                    {% endif %}
                </form>
            </section>
        </div>
    </div>
    {% endblock %}
    {% block scripts %}
    {% load static %}
    <link rel="stylesheet" type="text/css" href="{% static 'app/site.css' %}"/>
    {% endblock %}
    
  9. app/templates/app Na pasta, crie um ficheiro, loginpartial.html com os conteúdos abaixo.

    {% if user.is_authenticated  %}
    <form id="logoutForm" action="/applogout" method="post" class="navbar-right">
        {% csrf_token %}
        <ul class="nav navbar-nav navbar-right">
            <li><span class="navbar-brand">Hello {{ user.username }}!</span></li>
            <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
        </ul>
    </form>
    {% else %}
    <ul class="nav navbar-nav navbar-right">
        <li><a href="{% url 'login' %}">Log in</a></li>
    </ul>
    {% endif %}
    
  10. app Na pasta, crie um ficheiro, forms.py com os conteúdos abaixo.

    from django import forms
    from django.contrib.auth.forms import AuthenticationForm
    from django.utils.translation import ugettext_lazy as _
    
    class BootstrapAuthenticationForm(AuthenticationForm):
        """Authentication form which uses boostrap CSS."""
        username = forms.CharField(max_length=254,
                                   widget=forms.TextInput({
                                       'class': 'form-control',
                                       'placeholder': 'User name'}))
        password = forms.CharField(label=_("Password"),
                                   widget=forms.PasswordInput({
                                       'class': 'form-control',
                                       'placeholder':'Password'}))
    
  11. Modifique app/models.py para corresponder ao seguinte código.

    from django.db import models
    from django.contrib.auth.models import User
    
    # In this web app a Microsoft Advertising user maps a Django web user to a refresh token.
    
    class BingAdsUser(models.Model):
        user = models.OneToOneField(User, on_delete=models.PROTECT)
        refresh_token = models.CharField(max_length=200)
    
        # def __unicode__(self):              # __unicode__ on Python 2
        #     return self.refresh_token
        def __str__(self):              # __str__ on Python 3
            return self.refresh_token
    
  12. Modifique app/views.py para corresponder ao seguinte código.

    from django.http import HttpRequest, HttpResponse
    from django.shortcuts import render
    from django.template.loader import get_template, render_to_string
    from web_project import settings
    from datetime import datetime
    from django.shortcuts import redirect
    from django.contrib.auth import authenticate, login, logout, get_user_model
    from django.contrib.auth.models import User
    from app.models import BingAdsUser
    from bingads import *
    
    # import logging
    # logging.basicConfig(level=logging.INFO)
    # logging.getLogger('suds.client').setLevel(logging.DEBUG)
    # logging.getLogger('suds.transport').setLevel(logging.DEBUG)
    
    authorization_data = AuthorizationData(
        account_id=None, 
        customer_id=None, 
        developer_token=None, 
        authentication=None)
    
    customer_service=None
    
    def home(request):
        """
        If an authenticated user returns to this page after logging in, the appropriate 
        context is provided to index.html for rendering the page. 
        """
        assert isinstance(request, HttpRequest)
    
        # If the Django user has a refresh token stored, 
        # try to use it to get Microsoft Advertising data.
        if user_has_refresh_token(request.user.username):
            return redirect('/callback')
        else:
            return render(
                request,
                'app/index.html'
            )
    
    def callback(request):
        """Handles OAuth authorization, either via callback or direct refresh request."""
        assert isinstance(request, HttpRequest)
    
        authentication = OAuthWebAuthCodeGrant(
            client_id=settings.CLIENT_ID,
            client_secret=settings.CLIENT_SECRET, 
            redirection_uri=settings.REDIRECTION_URI,
            env=settings.ENVIRONMENT)
    
        return authorize_bing_ads_user(request, authentication)
    
    def authorize_bing_ads_user(request, authentication):
        assert isinstance(request, HttpRequest)
    
        global customer_service
        bingadsuser = None
    
        try:
            Users = get_user_model()
            user = User.objects.get(username=request.user.username)
        except User.DoesNotExist:
            return render(
                request,
                'app/index.html'
            )
    
        try:
            bingadsuser = user.bingadsuser
        except BingAdsUser.DoesNotExist:
            bingadsuser = BingAdsUser()
            bingadsuser.user = user
            pass
    
        try:
            # If we have a refresh token let's refresh the access token.
            if(bingadsuser is not None and bingadsuser.refresh_token != ""):
                authentication.request_oauth_tokens_by_refresh_token(bingadsuser.refresh_token)
                bingadsuser.refresh_token = authentication.oauth_tokens.refresh_token
    
            # If the current HTTP request is a callback from the Microsoft Account authorization server,
            # use the current request url containing authorization code to request new access and refresh tokens.
            elif (request.GET.get('code') is not None):
                authentication.request_oauth_tokens_by_response_uri(response_uri = request.get_full_path()) 
                bingadsuser.refresh_token = authentication.oauth_tokens.refresh_token
        except OAuthTokenRequestException:
            bingadsuser.refresh_token = ""  
    
        user.save()
        bingadsuser.save()
    
        # If there is no refresh token saved and no callback from the authorization server, 
        # then connect to the authorization server and request user consent.
        if (bingadsuser.refresh_token == ""):
            return redirect(authentication.get_authorization_endpoint())
    
        set_session_data(request, authentication)
    
        # At this point even if the user has valid Django web application credentials, 
        # we don't know whether they have access to Microsoft Advertising.
        # Let's test to see if they can call Bing Ads API service operations. 
    
        bing_ads_user = None
        accounts=[]
        errors=[]
    
        try:
            bing_ads_user = get_user(None)
            accounts = search_accounts_by_user_id(bing_ads_user.Id)['AdvertiserAccount']
        except WebFault as ex:
            errors=get_webfault_errors(ex)
            pass
    
        context = {
            'bingadsuser': bing_ads_user,
            'accounts': accounts,
            'errors': errors,
        }
        return render(
            request,
            'app/index.html',
            context
        )
    
    def revoke(request):
        """Deletes the refresh token for the user authenticated in the current session."""
        assert isinstance(request, HttpRequest)
    
        try:
            Users = get_user_model()
            user = User.objects.get(username=request.user.username)
            bingadsuser = user.bingadsuser
            if(bingadsuser is not None):
                bingadsuser.refresh_token = ""
                bingadsuser.save()
        except User.DoesNotExist:
            pass
        except BingAdsUser.DoesNotExist:
            pass
    
        clear_session_data(request)
    
        return render(
            request,
            'app/index.html'
        )
    
    def user_has_active_session(request):
        try:
            return True if request.session['is_authenticated'] else False 
        except KeyError:
            return False
    
    def user_has_refresh_token(username):
        try:
            Users = get_user_model()
            user = User.objects.get(username=username)
            bingadsuser = user.bingadsuser
            if(bingadsuser is not None and bingadsuser.refresh_token != ""):
                return True
        except User.DoesNotExist:
            return False
        except BingAdsUser.DoesNotExist:
            return False
    
    def set_session_data(request, authentication):
        global authorization_data
        global customer_service
    
        try:
            request.session['is_authenticated'] = True
    
            authorization_data.authentication = authentication
            authorization_data.developer_token = settings.DEVELOPER_TOKEN
    
            customer_service = ServiceClient(
                service='CustomerManagementService', 
                version=settings.API_VERSION,
                authorization_data=authorization_data,
                environment=settings.ENVIRONMENT
            )
    
        except KeyError:
            pass
        return None   
    
    def clear_session_data(request):
        global authorization_data
        global customer_service
    
        request.session['is_authenticated'] = False
    
        authorization_data = AuthorizationData(account_id=None, customer_id=None, developer_token=None, authentication=None)
        customer_service = None
    
    def applogout(request):
        logout(request)
        clear_session_data(request)
        return redirect('/')
    
    def get_user(user_id):
        ''' 
        Gets a Microsoft Advertising User object by the specified user ID.
    
        :param user_id: The Microsoft Advertising user identifier.
        :type user_id: long
        :return: The Microsoft Advertising user.
        :rtype: User
        '''
        global customer_service
    
        return customer_service.GetUser(UserId = user_id).User
    
    def search_accounts_by_user_id(user_id):
        ''' 
        Search for account details by UserId.
    
        :param user_id: The Microsoft Advertising user identifier.
        :type user_id: long
        :return: List of accounts that the user can manage.
        :rtype: Dictionary of AdvertiserAccount
        '''
    
        predicates={
            'Predicate': [
                {
                    'Field': 'UserId',
                    'Operator': 'Equals',
                    'Value': user_id,
                },
            ]
        }
    
        accounts=[]
    
        page_index = 0
        PAGE_SIZE=100
        found_last_page = False
    
        while (not found_last_page):
            paging=set_elements_to_none(customer_service.factory.create('ns5:Paging'))
            paging.Index=page_index
            paging.Size=PAGE_SIZE
            search_accounts_response = customer_service.SearchAccounts(
                PageInfo=paging,
                Predicates=predicates
            )
    
            if search_accounts_response is not None and hasattr(search_accounts_response, 'AdvertiserAccount'):
                accounts.extend(search_accounts_response['AdvertiserAccount'])
                found_last_page = PAGE_SIZE > len(search_accounts_response['AdvertiserAccount'])
                page_index += 1
            else:
                found_last_page=True
    
        return {
            'AdvertiserAccount': accounts
        }
    
    def set_elements_to_none(suds_object):
        for (element) in suds_object:
            suds_object.__setitem__(element[0], None)
        return suds_object
    
    def get_webfault_errors(ex):
        errors=[]
    
        if not hasattr(ex.fault, "detail"):
            raise Exception("Unknown WebFault")
    
        error_attribute_sets = (
            ["ApiFault", "OperationErrors", "OperationError"],
            ["AdApiFaultDetail", "Errors", "AdApiError"],
            ["ApiFaultDetail", "BatchErrors", "BatchError"],
            ["ApiFaultDetail", "OperationErrors", "OperationError"],
            ["EditorialApiFaultDetail", "BatchErrors", "BatchError"],
            ["EditorialApiFaultDetail", "EditorialErrors", "EditorialError"],
            ["EditorialApiFaultDetail", "OperationErrors", "OperationError"],
        )
    
        for error_attribute_set in error_attribute_sets:
            errors = get_api_errors(ex.fault.detail, error_attribute_set)
            if errors is not None:
                return errors
    
        return None
    
    def get_api_errors(error_detail, error_attribute_set):
        api_errors = error_detail
        for field in error_attribute_set:
            api_errors = getattr(api_errors, field, None)
        if api_errors is None:
            return None
    
        errors=[]
        if type(api_errors) == list:
            for api_error in api_errors:
                errors.append(api_error)
        else:
            errors.append(api_errors)
        return errors
    
  13. Substitua o conteúdo de web_project/urls.py pelo conteúdo abaixo. O urls.py ficheiro é onde especifica padrões para encaminhar diferentes URLs para as vistas adequadas. Por exemplo, o código abaixo mapeia o URL de raiz da aplicação ("") para a home função que acabou de adicionar a app/views.py:

    from django.contrib import admin
    from django.urls import path
    from app import views as app_views
    from django.contrib.auth import views as auth_views
    from datetime import datetime
    from django.conf.urls import include, url
    from app.forms import BootstrapAuthenticationForm
    from django.contrib.auth.views import HttpResponseRedirect
    
    from django.contrib import admin
    admin.autodiscover()
    
    urlpatterns = [
        url(r'^applogout', app_views.applogout, name='applogout'),
        url(r'^callback', app_views.callback, name='callback'),
        url(r'^revoke', app_views.revoke, name='revoke'),
        url(r'^$', app_views.home, name='home'),
        url(r'^login/$',
            auth_views.LoginView.as_view(
                template_name='app/login.html', 
                authentication_form=BootstrapAuthenticationForm,
                extra_context= {
                    'title':'Log in',
                    'year':datetime.now().year,
                }
            ),
            name='login'),
        url(r'^logout$',
            auth_views.LogoutView.as_view(),
            {
                'next_page': '/',
            },
            name='logout'),
    
        url(r'^admin/', admin.site.urls),
    ]
    
  14. Guarde todos os ficheiros modificados com Ctrl+K S.

  15. Execute python manage.py makemigrations para gerar scripts na pasta de migrações que migram a base de dados do estado atual para o novo estado.

  16. Execute python manage.py migrate para aplicar os scripts à base de dados real. Os scripts de migração registam efetivamente todas as alterações incrementais efetuadas aos modelos de dados (models.py) ao longo do tempo. Ao aplicar as migrações, o Django atualiza a base de dados para corresponder aos seus modelos. Uma vez que cada alteração incremental tem o seu próprio script, o Django pode migrar automaticamente qualquer versão anterior de uma base de dados (incluindo uma nova base de dados) para a versão atual. Como resultado, só precisa de se preocupar com os seus modelos no models.py, nunca com o esquema de base de dados subjacente ou com os scripts de migração. Deixaste o Django fazer essa parte!

  17. Crie uma conta de superutilizador na aplicação ao abrir um Terminal no VS Code para o seu ambiente virtual e, em seguida, executar o comando python manage.py createsuperuser --username=<username> --email=<email>, substituindo <username> e <email>, claro, pelas suas informações pessoais. Quando executa o comando, o Django pede-lhe para introduzir e confirmar a sua palavra-passe.

    Importante

    Não se esqueça da combinação de nome de utilizador e palavra-passe. Estas são as credenciais que utiliza para autenticar no portal de administração da aplicação Web.

  18. No Terminal do VS Code, novamente com o ambiente virtual ativado, execute o servidor de desenvolvimento com python manage.py runserver e abra um browser para http://127.0.0.1:8000/ ver uma página que compõe "Hello, Django".

  19. No browser, aceda a http://127.0.0.1:8000/admin/ e crie um novo utilizador Web django em Utilizadores. Isto é diferente das suas credenciais de utilizador do Microsoft Advertising, para que vários utilizadores do Microsoft Advertising possam iniciar sessão na sua aplicação separadamente.

    Django Administração

  20. Inicie sessão com o novo utilizador (e não com o super administrador) e deverá ver a opção para autenticar com uma conta Microsoft.

    Autenticar Conta Microsoft

  21. Depois de clicar em Autenticar Conta Microsoft , ser-lhe-á pedido que conceda as suas próprias permissões de aplicação Web para gerir as suas contas do Microsoft Advertising. Se consentir e se tiver acesso às contas do Microsoft Advertising, deverá ser redirecionado para uma vista dos seus nomes de conta e IDs.

See Also

Introdução à Utilização do Python com a API de Anúncios do Bing