Compartir a través de


Tutorial: Aplicación web de api de Bing Ads en Python

En este tutorial se explica cómo empezar a ejecutar una aplicación web de Microsoft Advertising mediante el SDK de Python de Bing Ads, el IDE de Visual Studio Code y el marco web de Django.

En este tutorial no se exploran varios detalles sobre django, como trabajar con modelos de datos y crear una interfaz administrativa. Para obtener instrucciones sobre esos aspectos, consulte la documentación de Django. Para obtener más información sobre cómo trabajar con Django en el terminal, editor y depurador de VS Code, consulte Uso de Django en Visual Studio Code. En este tutorial se toma en serio las instrucciones de configuración de Uso de Django en Visual Studio Code.

Introducción a la aplicación web de ejemplo

Al final de este tutorial, tendrá una aplicación web en ejecución en http://localhost la que autenticará sus credenciales de usuario de Microsoft Advertising y mostrará la información de usuario y cuentas. A continuación, puede agregar varios usuarios de aplicaciones web, que pueden habilitar el acceso para que la aplicación use sus credenciales de Microsoft Advertising. Esta aplicación web proporciona una asignación de uno a uno de un usuario de aplicación web, por ejemplo, ContosoUser a un usuario de Microsoft Advertising. Para obtener información sobre cómo modificar el modelo de datos, consulte la documentación de Django para obtener más información. Si el usuario de la aplicación web permite el acceso a sus cuentas de Microsoft Advertising con una cuenta de Microsoft, se almacena un token de actualización en la base de datos SQL Lite del servidor web.

Requisitos previos

Tendrá que tener instalado Visual Studio Code para seguir este tutorial. Para ejecutar la aplicación web de Django, puede usar Visual Studio Community o Visual Studio Professional; sin embargo, los pasos de configuración variarán de los de este tutorial.

Tendrá que tener Python 3 instalado desde python.org; Normalmente, use el botón Descargar Python 3.7.0 que aparece primero en la página (o lo que sea la versión más reciente). En Windows, asegúrese de que la ubicación del intérprete de Python está incluida en la variable de entorno PATH. Para comprobarlo, ejecute path en el símbolo del sistema. Si no se incluye la carpeta del intérprete de Python, abra Configuración de Windows, busque "entorno", seleccione Editar variables de entorno para la cuenta y, a continuación, edite la variable Path para incluir esa carpeta.

Tendrá que instalar el SDK de Python de Bing Ads y este tutorial le guiará por la instalación.

Necesitará el marco web de Django instalado para implementar la aplicación localmente y este tutorial le guiará por la instalación.

Necesitará al menos un usuario con credenciales de Microsoft Advertising y un token de desarrollador.

Tendrá que registrar una aplicación y tomar nota del identificador de cliente (id. de aplicación registrado) y del secreto de cliente (contraseña registrada). Tendrá que registrar una aplicación web (no nativa) para este ejemplo. Se le pedirá que registre una o varias direcciones URL de redireccionamiento y, para este tutorial, debe registrar http://localhost/callback. En su lugar, debe usar https cuando se implemente en un servidor de producción. Para obtener más información sobre cómo registrar una aplicación y el flujo de concesión de código de autorización, consulte Autenticación con OAuth.

Este tutorial se desarrolló en Windows. Aunque Windows no es necesario para ejecutar el ejemplo, algunos de los pasos siguientes variarán si usa otro sistema operativo, por ejemplo, Linux o MacOS.

Creación de un entorno de proyecto para Django

En esta sección creará un entorno virtual en el que django está instalado. El uso de un entorno virtual evita la instalación de Django en un entorno de Python global y proporciona un control exacto sobre las bibliotecas usadas en una aplicación.

  1. En el sistema de archivos, cree una carpeta de proyecto para este tutorial, como hello_django.

  2. En la hello_django carpeta, abra PowerShell o el shell de script favorito y use el siguiente comando para crear un entorno virtual denominado env basado en el intérprete actual:

    py -3 -m venv env
    
  3. Abra la carpeta del hello_django proyecto en VS Code ejecutando code .o ejecutando VS Code y usando el comando Abrircarpeta de archivo>.

    Abrir VS Code

  4. En VS Code, abra la paleta de comandos (Ver>paleta de comandos o Ctrl+Shift+P). A continuación, seleccione el comando Python: Select Interpreter (Python: seleccionar intérprete ).

  5. El comando presenta una lista de intérpretes disponibles que VS Code puede localizar automáticamente. La lista variará; Si no ve el intérprete deseado, consulte Configuración de entornos de Python. En la lista, seleccione el entorno virtual en la carpeta del proyecto que comienza con ./env o .\env:

    Selección del entorno virtual para Python

  6. Ejecutar terminal: nuevo terminal (Ctrl+Shift+ ` ) desde la paleta de comandos, que crea un terminal y activa automáticamente el entorno virtual mediante la ejecución de su script de activación.

    Nota:

    En Windows, si el tipo de terminal predeterminado es PowerShell, es posible que vea un error que indica que no se puede ejecutar activate.ps1 porque la ejecución de scripts está deshabilitada en el sistema. El error debe proporcionar un vínculo para obtener información sobre cómo permitir scripts. De lo contrario, use Terminal: seleccione Default Shell (Shell predeterminado) para establecer el valor predeterminado que prefiera.

    El entorno seleccionado aparece en la esquina inferior izquierda de la barra de estado de VS Code. Observe el indicador (venv) que le indica que usa un entorno virtual:

    Entorno seleccionado que se muestra en la barra de estado de VS Code

  7. Instale Django en el entorno virtual a través de pip en el terminal de VS Code:

    python -m pip install django
    
  8. Instale el SDK de Python de Bing Ads en el entorno virtual a través de pip en el terminal de VS Code:

    python -m pip install bingads
    

Ahora tiene un entorno virtual independiente listo para escribir código de Django y Microsoft Advertising.

Creación y ejecución de una aplicación de Django

En la terminología de Django, un "proyecto de Django" se compone de varios archivos de configuración de nivel de sitio junto con una o varias "aplicaciones" que se implementan en un host web para crear una aplicación web completa. Un proyecto de Django puede contener varias aplicaciones, cada una de las cuales normalmente tiene una función independiente en el proyecto, y la misma aplicación puede estar en varios proyectos de Django. Una aplicación, por su parte, es solo un paquete de Python que sigue ciertas convenciones que Django espera.

Para crear una aplicación de Django, es necesario crear primero el proyecto de Django para que actúe como contenedor de la aplicación y, a continuación, crear la propia aplicación. Para ambos propósitos, use la utilidad administrativa de Django, django-admin, que se instala al instalar el paquete de Django.

Creación del proyecto de Django

  1. En el terminal de VS Code donde está activado el entorno virtual, ejecute el siguiente comando:

    django-admin startproject web_project .
    

    Este startproject comando supone (mediante el uso de . al final) que la carpeta actual es la carpeta del proyecto y crea lo siguiente en ella:

    • manage.py: utilidad administrativa de la línea de comandos de Django para el proyecto. Ejecute comandos administrativos para el proyecto mediante python manage.py <command> [options].

    • Subcarpeta denominada web_project, que contiene los siguientes archivos:

      • __init__.py: un archivo vacío que indica a Python que esta carpeta es un paquete de Python.
      • wsgi.py: un punto de entrada para que los servidores web compatibles con WSGI sirvan al proyecto. Normalmente, este archivo se deja tal como está, ya que proporciona los enlaces para los servidores web de producción.
      • settings.py: contiene la configuración del proyecto de Django, que se modifica durante el desarrollo de una aplicación web.
      • urls.py: contiene una tabla de contenido para el proyecto de Django, que también se modifica en el transcurso del desarrollo.

      Proyecto web django proyecto

  2. Para comprobar el proyecto de Django, asegúrese de que el entorno virtual está activado y, a continuación, inicie el servidor de desarrollo de Django mediante el comando python manage.py runserver. El servidor se ejecuta en el puerto predeterminado 8000 y verá una salida similar a la siguiente en la ventana de salida del terminal de 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.
    

    Cuando se ejecuta el servidor por primera vez, se crea una base de datos SQLite predeterminada en el archivo db.sqlite3, que generalmente está pensada para fines de desarrollo, pero se puede usar en producción para aplicaciones web de bajo volumen. Además, el servidor web integrado de Django está diseñado solo para fines de desarrollo local. Sin embargo, al implementar en un host web, Django usa el servidor web del host en su lugar. El wsgi.py módulo del proyecto de Django se encarga de conectarse a los servidores de producción.

    Si desea usar un puerto diferente al 8000 predeterminado, simplemente especifique el número de puerto en la línea de comandos, como python manage.py runserver 5000.

  3. Ctrl+click la http://127.0.0.1:8000/ dirección URL de la ventana de salida del terminal de VS Code para abrir el explorador predeterminado en esa dirección. Si Django está instalado correctamente y el proyecto es válido, verá la página predeterminada que se muestra a continuación. La ventana Salida de VS Code también muestra el registro del servidor.

    Vista predeterminada del proyecto de Django vacío

  4. Cuando haya terminado, cierre la ventana del explorador y detenga el servidor en VS Code mediante Ctrl+C como se indica en la ventana de salida del terminal de VS Code.

Creación de una aplicación de Django para Microsoft Advertising

  1. En el terminal de VS Code con el entorno virtual activado, ejecute el comando de la utilidad administrativa en la carpeta del startapp proyecto (donde manage.py reside):

    python manage.py startapp app
    

    El comando crea una carpeta denominada app que contiene varios archivos de código y una subcarpeta. De ellos, suele trabajar con views.py (que contiene las funciones que definen páginas en la aplicación web) y models.py (que contiene clases que definen los objetos de datos). La utilidad administrativa de Django usa la migrations carpeta para administrar las versiones de la base de datos, como se describe más adelante en este tutorial. También están los archivos apps.py (configuración de la aplicación), admin.py (para crear una interfaz administrativa) y tests.py (para pruebas unitarias), que no se tratan aquí.

  2. En , app/settings.py agregue el código siguiente y establezca sus propios valores de CLIENT_ID, CLIENT_SECRET, DEVELOPER_TOKEN y ENVIRONMENT .

    """
    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. En app/settings.py , agregue app a la lista de aplicaciones instaladas.

        INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app',
    ]
    
  4. En el terminal de VS Code, cree las app/static/app carpetas y app/templates/app :

    (env) PS C:\dev\hello_django> mkdir app/static/app
    (env) PS C:\dev\hello_django> mkdir app/templates/app 
    
  5. En las app/static/app carpetas, cree un nuevo archivo denominado site.css y agregue el siguiente contenido.

    .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. En la app/templates/app carpeta, cree un archivo, index.html con el contenido siguiente.

    {% 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. En la app/templates/app carpeta, cree un archivo, layout.html con el contenido siguiente.

    <!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. En la app/templates/app carpeta, cree un archivo, login.html con el contenido siguiente.

    {% 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. En la app/templates/app carpeta, cree un archivo, loginpartial.html con el contenido siguiente.

    {% 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. En la app carpeta, cree un archivo, forms.py con el contenido siguiente.

    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 que coincida con el código siguiente.

    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 que coincida con el código siguiente.

    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. Reemplace el contenido de web_project/urls.py por el contenido siguiente. El urls.py archivo es donde se especifican patrones para enrutar distintas direcciones URL a sus vistas adecuadas. Por ejemplo, el código siguiente asigna la dirección URL raíz de la aplicación ("") a la home función que acaba de agregar 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 los archivos modificados con Ctrl+K S.

  15. Ejecute python manage.py makemigrations para generar scripts en la carpeta de migraciones que migra la base de datos de su estado actual al nuevo estado.

  16. Ejecute python manage.py migrate para aplicar los scripts a la base de datos real. Los scripts de migración registran de forma eficaz todos los cambios incrementales realizados en los modelos de datos (models.py) a lo largo del tiempo. Al aplicar las migraciones, Django actualiza la base de datos para que coincida con los modelos. Dado que cada cambio incremental tiene su propio script, Django puede migrar automáticamente cualquier versión anterior de una base de datos (incluida una nueva base de datos) a la versión actual. Como resultado, solo necesita preocuparse por los modelos de models.py, nunca con el esquema de base de datos subyacente ni con los scripts de migración. ¡Dejas que Django haga esa parte!

  17. Cree una cuenta de superusuario en la aplicación abriendo un Terminal en VS Code para el entorno virtual y, a continuación, ejecutando el comando python manage.py createsuperuser --username=<username> --email=<email>, reemplazando <username> y <email>, por supuesto, con su información personal. Al ejecutar el comando, Django le pide que escriba y confirme la contraseña.

    Importante

    Asegúrese de recordar la combinación de nombre de usuario y contraseña. Estas son las credenciales que se usan para autenticarse en el portal de administración de la aplicación web.

  18. En el terminal de VS Code, de nuevo con el entorno virtual activado, ejecute el servidor de desarrollo con python manage.py runserver y abra un explorador para http://127.0.0.1:8000/ ver una página que represente "Hello, Django".

  19. En el explorador web, vaya a http://127.0.0.1:8000/admin/ y cree un nuevo usuario web de Django en Usuarios. Esto es distinto de las credenciales de usuario de Microsoft Advertising, de modo que varios usuarios de Microsoft Advertising puedan iniciar sesión en la aplicación por separado.

    Django Administración

  20. Inicie sesión con el nuevo usuario (no con el superadministrador) y debería ver la opción para autenticarse con una cuenta de Microsoft.

    Autenticación de la cuenta microsoft

  21. Después de hacer clic en Autenticar cuenta microsoft , se le pedirá que conceda permisos a su propia aplicación web para administrar sus cuentas de Microsoft Advertising. Si da su consentimiento y tiene acceso a cuentas de Microsoft Advertising, se le redirigirá a una vista de los nombres de cuenta y los identificadores.

Consulta también

Introducción al uso de Python con Bing Ads API