บทช่วยสอน: แอปพลิเคชันเว็บ Bing Ads API ใน Python

บทช่วยสอนนี้สํารวจวิธีการเริ่มต้นใช้งานเว็บแอปพลิเคชัน Microsoft Advertising โดยใช้ Bing Ads Python SDK, Visual Studio Code IDE และ Django web framework

บทช่วยสอนนี้ไม่ได้สํารวจรายละเอียดต่างๆ เกี่ยวกับ Django เอง เช่น การทํางานกับตัวแบบข้อมูลและการสร้างส่วนติดต่อการดูแลระบบ สําหรับคําแนะนําเกี่ยวกับแง่มุมเหล่านั้น ให้ดูเอกสารประกอบ Django สําหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีทํางานกับ Django ในเทอร์มินัล VS Code ตัวแก้ไข และดีบักเกอร์ ให้ดู ใช้ Django ใน Visual Studio Code บทช่วยสอนนี้ยืมมาจากคําแนะนําในการตั้งค่าใน ใช้ Django ใน Visual Studio Code เป็นอย่างมาก

ตัวอย่างภาพรวมโปรแกรมประยุกต์เว็บ

เมื่อจบบทช่วยสอนนี้ คุณจะมีแอปพลิเคชัน http://localhost บนเว็บที่ทํางานอยู่ซึ่งจะรับรองความถูกต้องของข้อมูลประจําตัวผู้ใช้ Microsoft Advertising ของคุณและแสดงข้อมูลผู้ใช้และบัญชีของคุณ จากนั้นคุณสามารถเพิ่มผู้ใช้เว็บแอปพลิเคชันหลายคนที่สามารถเปิดใช้งานการเข้าถึงสําหรับแอปพลิเคชันของคุณเพื่อใช้ข้อมูลประจําตัว Microsoft Advertising ของพวกเขา เว็บแอปพลิเคชันนี้จะให้การแมปผู้ใช้เว็บแอปพลิเคชันแบบหนึ่ง-ต่อ-หนึ่ง เช่น ContosoUser กับผู้ใช้ Microsoft Advertising สําหรับข้อมูลเกี่ยวกับวิธีการปรับเปลี่ยนรูปแบบข้อมูล โปรดดู เอกสารประกอบ Django สําหรับข้อมูลเพิ่มเติม หากผู้ใช้แอปพลิเคชันเว็บของคุณเปิดใช้งานการเข้าถึงบัญชี Microsoft Advertising ของตนด้วยบัญชี Microsoft โทเค็นรีเฟรชจะถูกจัดเก็บไว้ในฐานข้อมูล SQL Lite บนเว็บเซิร์ฟเวอร์ของคุณ

ข้อกำหนดเบื้องต้น

คุณจะต้องติดตั้ง Visual Studio Code เพื่อทําตามบทช่วยสอนนี้ เมื่อต้องการเรียกใช้เว็บแอป Django คุณสามารถใช้ Visual Studio Community หรือ Visual Studio Professional อย่างไรก็ตาม ขั้นตอนการตั้งค่าจะแตกต่างจากขั้นตอนในบทช่วยสอนนี้

คุณจะต้องติดตั้ง Python 3 จาก python.org โดยทั่วไปแล้วใช้ปุ่ม ดาวน์โหลด Python 3.7.0 ที่ปรากฏก่อนบนหน้า (หรืออะไรก็ตามที่เป็นเวอร์ชันล่าสุด) บน Windows ตรวจสอบให้แน่ใจว่าตําแหน่งที่ตั้งของตัวแปล Python รวมอยู่ในตัวแปรสภาพแวดล้อม PATH ของคุณ คุณสามารถตรวจสอบได้โดยเรียกใช้ path ที่พร้อมท์คําสั่ง หากไม่มีโฟลเดอร์ของตัวแปลภาษา Python ให้เปิดการตั้งค่า Windows ค้นหา "สภาพแวดล้อม" เลือก แก้ไขตัวแปรสภาพแวดล้อมสําหรับบัญชีของคุณ จากนั้นแก้ไขตัวแปร เส้นทาง เพื่อรวมโฟลเดอร์นั้น

คุณจะต้อง ติดตั้งBing Ads Python SDK และบทช่วยสอนนี้จะแนะนําคุณตลอดการติดตั้ง

คุณจะต้องติดตั้ง เฟรมเวิร์กเว็บ Django เพื่อปรับใช้แอปพลิเคชันในเครื่อง และบทช่วยสอนนี้จะแนะนําคุณตลอดการติดตั้ง

คุณจะต้องมีผู้ใช้อย่างน้อยหนึ่งคนที่มีข้อมูลประจําตัว Microsoft Advertising และโทเค็นนักพัฒนา

คุณจะต้องลงทะเบียนแอปพลิเคชันและจดบันทึก ID ไคลเอ็นต์ (ID แอปพลิเคชันที่ลงทะเบียน) และความลับของไคลเอ็นต์ (รหัสผ่านที่ลงทะเบียน) คุณจะต้องลงทะเบียนเว็บแอป (ไม่ใช่ดั้งเดิม) สําหรับตัวอย่างนี้ คุณจะถูกขอให้ลงทะเบียน URL การเปลี่ยนเส้นทางอย่างน้อยหนึ่งรายการ และสําหรับบทช่วยสอนนี้ คุณควรลงทะเบียนhttp://localhost/callback คุณควรใช้ https แทนเมื่อปรับใช้กับเซิร์ฟเวอร์การผลิต สําหรับรายละเอียดเพิ่มเติมเกี่ยวกับการลงทะเบียนแอปพลิเคชันและขั้นตอนการให้รหัสการตรวจสอบ ให้ดู การรับรองความถูกต้องด้วย OAuth

บทช่วยสอนนี้ได้รับการพัฒนาบน Windows แม้ว่า Windows จะไม่ต้องใช้เพื่อเรียกใช้ตัวอย่าง แต่ขั้นตอนด้านล่างบางขั้นตอนจะแตกต่างกันออกไปหากคุณใช้ระบบปฏิบัติการอื่น เช่น Linux หรือ MacOS

สร้างสภาพแวดล้อมโครงการสําหรับ Django

ในส่วนนี้คุณสร้างสภาพแวดล้อมเสมือนที่ติดตั้ง Django การใช้สภาพแวดล้อมเสมือนจะหลีกเลี่ยงการติดตั้ง Django ในสภาพแวดล้อม Python ส่วนกลาง และช่วยให้คุณควบคุมไลบรารีที่ใช้ในแอปพลิเคชันได้อย่างแม่นยํา

  1. บนระบบไฟล์ของคุณ ให้สร้างโฟลเดอร์โครงการสําหรับบทช่วยสอนนี้ เช่นhello_django

  2. ใน hello_django โฟลเดอร์ ให้เปิด Powershell หรือเชลล์สคริปต์ที่คุณชื่นชอบ และใช้คําสั่งต่อไปนี้เพื่อสร้างสภาพแวดล้อมเสมือนที่ตั้งชื่อตาม env ล่ามปัจจุบันของคุณ:

    py -3 -m venv env
    
  3. เปิดโฟลเดอร์โครงการhello_djangoใน VS Code โดยการเรียกใช้ code .หรือโดยการเรียกใช้ VS Code และใช้คําสั่ง เปิดแฟ้มโฟลเดอร์>

    เปิด VS Code

  4. ใน VS Code ให้เปิดชุดคําสั่ง (ดูชุด>คําสั่งCtrl+Shift+Pหรือ) จากนั้นเลือกคําสั่ง Python: Select Interpreter

  5. คําสั่งจะแสดงรายการตัวแปลที่พร้อมใช้งานที่ VS Code สามารถค้นหาได้โดยอัตโนมัติ รายการของคุณจะแตกต่างกันไป หากคุณไม่เห็นตัวแปลที่ต้องการ โปรดดูการกําหนดค่าสภาพแวดล้อม Python จากรายการ ให้เลือกสภาพแวดล้อมเสมือนในโฟลเดอร์โครงการของคุณที่เริ่มต้นด้วย ./env หรือ .\env:

    การเลือกสภาพแวดล้อมเสมือนสําหรับ Python

  6. เรียกใช้ เทอร์มินัล: เทอร์มินัลใหม่ ()Ctrl+Shift+ ` จากชุดคําสั่ง ซึ่งจะสร้างเทอร์มินัลและเปิดใช้งานสภาพแวดล้อมเสมือนโดยอัตโนมัติโดยการเรียกใช้สคริปต์การเปิดใช้งาน

    หมายเหตุ

    บน Windows ถ้าชนิดเทอร์มินัลเริ่มต้นของคุณคือ PowerShell คุณอาจเห็นข้อผิดพลาดที่ไม่สามารถเรียกใช้ activate.ps1 ได้เนื่องจากการเรียกใช้สคริปต์ถูกปิดใช้งานในระบบ ข้อผิดพลาดควรให้ลิงก์สําหรับข้อมูลเกี่ยวกับวิธีการอนุญาตสคริปต์ หรือใช้ เทอร์มินัล: เลือกเชลล์ค่าเริ่มต้นเพื่อ ตั้งค่าเริ่มต้นที่คุณต้องการ

    สภาพแวดล้อมที่เลือกจะปรากฏที่มุมซ้ายล่างของแถบสถานะ VS Code ให้สังเกตตัวบ่งชี้ (venv) ที่บอกคุณว่าคุณกําลังใช้สภาพแวดล้อมเสมือน:

    สภาพแวดล้อมที่เลือกแสดงในแถบสถานะ VS Code สภาพแวดล้อม

  7. ติดตั้ง Django ในสภาพแวดล้อมเสมือนผ่าน pip ในเทอร์มินัล VS Code:

    python -m pip install django
    
  8. ติดตั้ง Bing Ads Python SDK ในสภาพแวดล้อมเสมือนผ่าน pip ในเทอร์มินัล VS Code:

    python -m pip install bingads
    

ตอนนี้คุณมีสภาพแวดล้อมเสมือนที่มีตัวเองที่พร้อมสําหรับการเขียนโค้ด Django และ Microsoft Advertising

สร้างและเรียกใช้แอป Django

ในคําศัพท์ของ Django "โครงการ Django" ประกอบด้วยไฟล์การกําหนดค่าระดับไซต์หลายไฟล์พร้อมกับ "แอป" อย่างน้อยหนึ่งรายการที่คุณปรับใช้กับโฮสต์เว็บเพื่อสร้างแอปพลิเคชันเว็บเต็มรูปแบบ โครงการ Django สามารถมีหลายแอป ซึ่งแต่ละแอปจะมีฟังก์ชันอิสระในโครงการ และแอปเดียวกันสามารถอยู่ในโครงการ Django หลายโครงการได้ ในส่วนของแอปเป็นเพียงแพ็คเกจ Python ที่เป็นไปตามข้อตกลงบางอย่างที่ Django คาดหวัง

ในการสร้างแอป Django คุณจําเป็นต้องสร้างโครงการ Django เพื่อทําหน้าที่เป็นคอนเทนเนอร์สําหรับแอปก่อน จากนั้นจึงสร้างแอปขึ้นเอง สําหรับวัตถุประสงค์ทั้งสองอย่าง คุณใช้โปรแกรมอรรถประโยชน์การดูแล django-adminระบบ Django ซึ่งติดตั้งเมื่อคุณติดตั้งแพคเกจ Django

สร้างโครงการ Django

  1. ในเทอร์มินัล VS Code ที่สภาพแวดล้อมเสมือนของคุณเปิดใช้งานอยู่ ให้เรียกใช้คําสั่งต่อไปนี้:

    django-admin startproject web_project .
    

    คําสั่งนี้จะ startproject ถือว่า (โดยการใช้ที่ . ส่วนท้าย) ว่าโฟลเดอร์ปัจจุบันเป็นโฟลเดอร์โครงการของคุณ และสร้างรายการต่อไปนี้อยู่ภายใน:

    • manage.py: โปรแกรมอรรถประโยชน์การดูแลระบบบรรทัดคําสั่ง Django สําหรับโครงการ คุณเรียกใช้คําสั่งการดูแลระบบสําหรับโครงการโดยใช้python manage.py <command> [options]

    • โฟลเดอร์ย่อยที่ชื่อ web_projectซึ่งมีไฟล์ต่อไปนี้:

      • __init__.py: ไฟล์เปล่าที่บอก Python ว่าโฟลเดอร์นี้เป็นแพคเกจ Python
      • wsgi.py: จุดเข้าสําหรับเว็บเซิร์ฟเวอร์ที่เข้ากันได้กับ WSGI เพื่อให้บริการโครงการของคุณ โดยทั่วไป คุณจะปล่อยไฟล์นี้ไว้ เนื่องจากไฟล์นี้มีจุดเชื่อมต่อสําหรับเว็บเซิร์ฟเวอร์การผลิต
      • settings.py: มีการตั้งค่าสําหรับโครงการ Django ซึ่งคุณปรับเปลี่ยนในระหว่างการพัฒนาเว็บแอป
      • urls.py: มีสารบัญสําหรับโครงการ Django ซึ่งคุณแก้ไขในระหว่างการพัฒนาด้วย

      Django Web Project

  2. ในการตรวจสอบโครงการ Django ตรวจสอบให้แน่ใจว่าสภาพแวดล้อมเสมือนของคุณเปิดใช้งานอยู่ จากนั้นเริ่มเซิร์ฟเวอร์การพัฒนาของ Django โดยใช้คําสั่งpython manage.py runserver เซิร์ฟเวอร์ทํางานบนพอร์ตเริ่มต้น 8000 และคุณจะเห็นผลลัพธ์ดังต่อไปนี้ในหน้าต่างผลลัพธ์ของเทอร์มินัล 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.
    

    เมื่อคุณเรียกใช้เซิร์ฟเวอร์เป็นครั้งแรก ระบบจะสร้างฐานข้อมูล SQLite เริ่มต้นในไฟล์ db.sqlite3ซึ่งโดยทั่วไปแล้วมีไว้สําหรับวัตถุประสงค์ในการพัฒนา แต่สามารถใช้ในการผลิตสําหรับเว็บแอปปริมาณน้อยได้ นอกจากนี้ เว็บเซิร์ฟเวอร์ในตัวของ Django ยังมีไว้เพื่อวัตถุประสงค์ในการพัฒนาภายในเครื่องเท่านั้น อย่างไรก็ตาม เมื่อคุณปรับใช้กับโฮสต์เว็บ Django จะใช้เว็บเซิร์ฟเวอร์ของโฮสต์แทน wsgi.pyโมดูลในโครงการ Django ดูแลการเชื่อมต่อกับเซิร์ฟเวอร์การผลิต

    ถ้าคุณต้องการใช้พอร์ตอื่นที่ไม่ใช่ 8000 ค่าเริ่มต้น ให้ระบุหมายเลขพอร์ตบนบรรทัดคําสั่ง เช่นpython manage.py runserver 5000

  3. Ctrl+click URL http://127.0.0.1:8000/ ในหน้าต่างผลลัพธ์ของเทอร์มินัล VS Code เพื่อเปิดเบราว์เซอร์เริ่มต้นของคุณไปยังที่อยู่นั้น ถ้า Django ได้รับการติดตั้งอย่างถูกต้องและโครงการถูกต้อง คุณจะเห็นหน้าเริ่มต้นที่แสดงอยู่ด้านล่าง หน้าต่างผลลัพธ์ VS Code จะแสดงบันทึกเซิร์ฟเวอร์ด้วย

    มุมมองเริ่มต้นของโครงการ Django ที่ว่างเปล่า

  4. เมื่อคุณทําเสร็จแล้ว ให้ปิดหน้าต่างเบราว์เซอร์และหยุดเซิร์ฟเวอร์ใน VS Code โดยใช้ตาม Ctrl+C ที่ระบุไว้ในหน้าต่างผลลัพธ์ของเทอร์มินัล VS Code

สร้างแอป Django สําหรับ Microsoft Advertising

  1. ในเทอร์มินัล VS Code ที่มีสภาพแวดล้อมเสมือนของคุณเปิดใช้งานอยู่ ให้เรียกใช้คําสั่งของโปรแกรม startapp อรรถประโยชน์การดูแลระบบในโฟลเดอร์โครงการของคุณ (ที่มี manage.py สถานที่อยู่):

    python manage.py startapp app
    

    คําสั่งจะสร้างโฟลเดอร์ที่มีชื่อไฟล์ app โค้ดจํานวนหนึ่งและโฟลเดอร์ย่อยหนึ่งโฟลเดอร์ ในจํานวนเหล่านี้ คุณจะทํางาน views.py บ่อยด้วย (ที่มีฟังก์ชันที่กําหนดหน้าใน Web App ของคุณ) และ models.py (ที่มีคลาสที่กําหนดวัตถุข้อมูลของคุณ) migrationsโฟลเดอร์นี้ถูกใช้โดยโปรแกรมอรรถประโยชน์การดูแลระบบของ Django เพื่อจัดการเวอร์ชันฐานข้อมูลตามที่กล่าวไว้ในส่วนท้ายของบทช่วยสอนนี้ นอกจากนี้ยังมีไฟล์ apps.py (การกําหนดค่าแอป) ( admin.py สําหรับการสร้างส่วนติดต่อการดูแลระบบ) และ tests.py (สําหรับการทดสอบหน่วย) ซึ่งไม่ได้กล่าวถึงในบทความนี้

  2. ภายใน app/settings.py ให้เพิ่มโค้ดต่อไปนี้และตั้งค่า CLIENT_ID,CLIENT_SECRET,DEVELOPER_TOKEN และสภาพแวดล้อมของคุณเอง

    """
    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/settings.py เพิ่ม app ไปยังรายการแอปที่ติดตั้ง

        INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app',
    ]
    
  4. ในเทอร์มินัล VS Code ให้สร้างโฟลเดอร์ app/static/app และ:app/templates/app

    (env) PS C:\dev\hello_django> mkdir app/static/app
    (env) PS C:\dev\hello_django> mkdir app/templates/app 
    
  5. ภายใน app/static/app โฟลเดอร์ ให้สร้างไฟล์ใหม่ที่ชื่อ site.css และเพิ่มเนื้อหาต่อไปนี้

    .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 โฟลเดอร์ ให้สร้างไฟล์ที่มี index.html เนื้อหาด้านล่าง

    {% 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 โฟลเดอร์ ให้สร้างไฟล์ที่มี layout.html เนื้อหาด้านล่าง

    <!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 โฟลเดอร์ ให้สร้างไฟล์ที่มี login.html เนื้อหาด้านล่าง

    {% 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 โฟลเดอร์ ให้สร้างไฟล์ที่มี loginpartial.html เนื้อหาด้านล่าง

    {% 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 โฟลเดอร์ ให้สร้างไฟล์ที่มี forms.py เนื้อหาด้านล่าง

    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. ปรับเปลี่ยน app/models.py ให้ตรงกับโค้ดต่อไปนี้

    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. ปรับเปลี่ยน app/views.py ให้ตรงกับโค้ดต่อไปนี้

    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. แทนที่เนื้อหาด้วย web_project/urls.py เนื้อหาด้านล่าง urls.pyไฟล์นี้คือตําแหน่งที่คุณระบุรูปแบบเพื่อกําหนดเส้นทาง URL ต่างๆ ไปยังมุมมองที่เหมาะสม ตัวอย่างเช่น โค้ดด้านล่างจะแมป URL รากของแอป ("") กับ home ฟังก์ชันที่คุณเพิ่งเพิ่มไป 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. บันทึกไฟล์ที่ปรับเปลี่ยนทั้งหมดด้วยCtrl+K S

  15. เรียกใช้ python manage.py makemigrations เพื่อสร้างสคริปต์ในโฟลเดอร์การโยกย้ายที่โยกย้ายฐานข้อมูลจากสถานะปัจจุบันไปยังสถานะใหม่

  16. เรียกใช้ python manage.py migrate เพื่อนําสคริปต์ไปใช้กับฐานข้อมูลจริง สคริปต์การโยกย้ายจะบันทึกการเปลี่ยนแปลงที่เพิ่มขึ้นทั้งหมดที่คุณทํากับตัวแบบข้อมูลของคุณ ()models.py อย่างมีประสิทธิภาพเมื่อเวลาผ่านไป โดยการนําการโยกย้ายไปใช้ Django จะอัปเดตฐานข้อมูลให้ตรงกับโมเดลของคุณ เนื่องจากการเปลี่ยนแปลงที่เพิ่มขึ้นแต่ละครั้งมีสคริปต์ของตัวเอง Django จึงสามารถย้ายฐานข้อมูลเวอร์ชันก่อนหน้า (รวมถึงฐานข้อมูลใหม่) ไปยังเวอร์ชันปัจจุบันได้โดยอัตโนมัติ ด้วยเหตุนี้ คุณจึงต้องกังวลกับโมเดลของคุณใน models.py เท่านั้น ไม่ต้องกังวลกับ Schema ฐานข้อมูลพื้นฐานหรือสคริปต์การโยกย้าย คุณปล่อยให้ Django ทําส่วนนั้น!

  17. สร้างบัญชีซูเปอร์ผู้ใช้ในแอปโดยการเปิดเทอร์มินัลใน VS Code สําหรับสภาพแวดล้อมเสมือนของคุณ จากนั้นเรียกใช้คําสั่ง python manage.py createsuperuser --username=<username> --email=<email>แทนที่และ <username> แน่นอน <email>ด้วยข้อมูลส่วนบุคคลของคุณ เมื่อคุณเรียกใช้คําสั่ง Django จะพร้อมท์ให้คุณใส่และยืนยันรหัสผ่านของคุณ

    สิ่งสำคัญ

    อย่าลืมรวมชื่อผู้ใช้และรหัสผ่านของคุณ นี่คือข้อมูลประจําตัวที่คุณใช้เพื่อรับรองความถูกต้องในพอร์ทัลผู้ดูแลระบบของเว็บแอป

  18. ในเทอร์มินัล VS Code เมื่อเปิดใช้งานสภาพแวดล้อมเสมือนอีกครั้ง ให้เรียกใช้เซิร์ฟเวอร์การพัฒนาและ python manage.py runserver เปิดเบราว์เซอร์เพื่อดู http://127.0.0.1:8000/ หน้าที่แสดง "สวัสดี Django"

  19. ในเว็บเบราว์เซอร์ ให้ไปที่และสร้างhttp://127.0.0.1:8000/admin/ผู้ใช้เว็บ Django ใหม่ภายใต้ผู้ใช้ ซึ่งแตกต่างจากข้อมูลประจําตัวผู้ใช้ Microsoft Advertising ของคุณ ดังนั้นผู้ใช้ Microsoft Advertising หลายคนจึงสามารถเข้าสู่ระบบแอปของคุณแยกต่างหากได้

    Django AdminDjango

  20. เข้าสู่ระบบด้วยผู้ใช้ใหม่ (ไม่ใช่ผู้ดูแลระบบขั้นสูง) และคุณควรเห็นตัวเลือกในการรับรองความถูกต้องด้วยบัญชี Microsoft

    รับรองความถูกต้องบัญชี Microsoft

  21. หลังจากที่คุณคลิก รับรองความถูกต้องบัญชี Microsoft คุณจะได้รับพร้อมท์เพื่อให้สิทธิ์เว็บแอปของคุณเองในการจัดการบัญชี Microsoft Advertising ของคุณ หากคุณยินยอม และหากคุณมีสิทธิ์เข้าถึงบัญชี Microsoft Advertising คุณควรจะถูกเปลี่ยนเส้นทางไปยังมุมมองของชื่อบัญชีและ ID ของคุณ

ดูเพิ่มเติม

เริ่มต้นใช้งาน Python ด้วย Bing Ads API