Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a Python Cloud-Native Webapp, Part 1: Deploying to Azure App Service

0.00/5 (No votes)
5 Jan 2022 1  
In this article we’ll create a more traditional web application using the Flash library and Azure WebApp.
Here we create a web app service in Azure that hosts a Python-based Flask task management application.

In the previous article of this series, we set up our Python environment, code editor, and Azure cloud environment. This setup enabled us to build simple, asynchronous APIs in Python using the FastAPI library.

We’ll expand our initial environment by building a more traditional web server application by using the Flask framework. We’ll make a functional yet straightforward ticket tracker web application that lets users add, view, and edit help desk tickets. After we create the web application get it running locally, we’ll deploy it to Azure right from a code editor.

Let’s get started.

Preparing for Flask

To follow along, you should already have set up an Azure environment, locally configured Python 3.9+, downloaded Visual Studio Code (VS Code), and installed VS Code’s Python and Azure extensions (the Azure extension is part of the Azure Function extension). If you don’t already have all this, refer to the previous article to configure the primary environment.

We still need to do a little more preparation before we get into the code.

Let’s open VS Code and create a new project to begin this tutorial. VS Code stores all its project configurations within the base folder we open, so we click File > Open Folder, and select /create an empty folder.

When this empty folder is open, we’ll add a new VS Code extension to help deploy and manage Azure App Service resources. To do this, we open the Extensions sidebar on the left (using Ctrl+Shift+X or by clicking the icon), find the Azure App Service extension, and Install it.

Then, we install the Flask framework by opening a new terminal window using CTRL+Shift+` or by clicking the top Terminal menu:

then New Terminal, and using the Python command:

Python
pip install flask

Then, we’re ready to begin creating our application.

Building a Basic Web Application

Flask works similarly to FastAPI, except it has been around longer, and its creators built it using the older, synchronous Web Server Gateway Interface (WGSI) standard. FastAPI uses the new Asynchronos Server Gateway Interface (ASGI) specification for asynchronous and synchronous applications. It makes sense to build a more traditional web application that uses a web server resource in Flask, but you could still do something similar in FastAPI.

To begin, let’s build a simple "Hello World" page again in Flask. We’ll ensure we can run it locally then deploy it to Azure App Service.

First, in our directory, we create an app.py file that we'll use as the entry point into our application. For now, we'll just code a "Hello World" response for when the user goes to the root of "/." We use the following code:

Python
from flask import Flask

app = Flask(__name__)
 
@app.route("/")
def home():
    return "Hello World!"

The first two lines import the Flask library and set up a rudimentary Flask server to use with the @app variable.

Next, we get the Flask server to listen to GET requests on the "/" route and execute the home function, returning the text "Hello World."

To test this code, we need to configure some launch settings in VS Code. First, we go to the Run and Debug menu (CTRL+SHIFT+D) on the left side and click create a launch.json file. This JSON file contains code launch configurations. It has some boilerplate code we can use to provide a generic debug environment, as follows:

JavaScript
{
    "name": "Python: Flask",
    "type": "python",
    "request": "launch",
    "module": "flask",
    "env": {
        "FLASK_APP": "app.py",
        "FLASK_ENV": "development",
        "FLASK_DEBUG": "0"
    },
    "args": [
        "run",
        "--no-debugger",
        "--no-reload"
    ],
    "jinja": true
}

After we save this file, we should now run our code and have a server running on port 5000. When we navigate to the local site http://127.0.0.1:5000, we get a simple "Hello World" page.

Deploying to Azure App Service

Now that we have a locally-running Flask application, let’s deploy it to Azure App Service before building out more functions. First, we need to create a requirements file so that our Python runtime can download dependencies. The easiest way to do this is to open a new Terminal (CTRL+SHIFT+`) from within VS Code and use the following command in the root directory:

Python
pip freeze > requirements.txt

Next, we create our web application by clicking on the Azure (CTRL+SHIFT+A) menu item and, under the App Service section, clicking the plus (+) button to Create a New Web App. We provide the subscription to create it in, a unique name (like "pythonflasktutorialwebapp"), the runtime stack we are using (Python 3.9), and the tier (we use the Free tier, but you could use basic (B1) or premium (P1v2) if you require more resources). Then, VS Code initiates creating the service.

Our subscription will have a new web application with various services and configuration options available when this process is complete. We can also right-click on the web application and go to Browse Website to open the default web application page. Let's replace this default page now by deploying our application to our app service.

Because we’re using a Flask application with an app.py entry point, deployment is straightforward. If you have a different setup or want other specific items, you may need a custom startup file.

We right-click again on the web application to deploy this application, select the Deploy to Web App option, then choose our code folder. VS Code asks to override the current deployment and always deploy the workspace to a web application. Say "yes" if you want to deploy to the cloud service continuously.

When the deployment completes, we click the Browse Website option, opening our current "Hello World Flask" application.

Adding Pages to the Application

Now that we have configured all our components, built a basic flask application, and prepared our Azure deployments, let’s create our actual application.

We’ll first build a couple of HTML pages to replace the text we’ve been outputting so far. To do this, we use a templating engine called Jinja that Flask already requires. We want a standard header and footer for our task application, with content that changes as we move through pages. We’ll also begin with three pages: a list of tasks, a static view of a task, and a form to create or edit tasks.

First, we create a new folder called "templates" to store our pages’ HTML templates that Jinja uses by default. Next, we create a "layout.html" file in our templates directory to specify the main container for our application using the following code:

HTML
<!DOCTYPE html>
<html>
    <head>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
        <nav class="navbar sticky-top navbar-dark bg-dark">
            <div class="container-fluid justify-content-center">
                <a class="navbar-brand" href="#">Flask Task Manager</a>
            </div>
        </nav>
    </head>
    <div><p></p></div>
    <main>
        {% block body %}
        {% endblock %}
    </main>
    <div></div>
    <div class="container">
        <footer class="d-flex flex-wrap justify-content-center align-items-center py-3 my-4 border-top">
          <p class="col-md-4 mb-0 text-muted ">Flask Task Manager Tutorial Application</p>
        </footer>
      </div>
</html>

This standard HTML template uses Bootstrap for theming, with one exception: Our main div contains a block body wrapper that our content will switch in and out of as required.

Let’s build our placeholder task list page. It will be the first page to replace this block body content. We create a home.html file in our template’s directory with the following code:

HTML
{% extends "layout.html" %}
{% block body %}
    <div class="container px-4 py-5">
        <h2 class="pb-2 border-bottom">Current Tasks</h2>
        <div class="row row-cols-1 row-cols-sm-1 row-cols-md-2 row-cols-lg-3 g-4 py-5">
            <!-- Repeat Block for Each Task-->
            <div class="col d-flex align-items-center card">
                <div class="card-body">
                    <h4 class="fw-bold mb-0">Task Name 01</h4>
                    <p>General description of the task</p>
                    <a href="#" class="btn btn-primary">View Task</a>
                    <a href="#" class="btn btn-primary">Edit Task</a>
                </div>
            </div>
            <!-- End Repeat -->
        </div>
    </div>
{% endblock %}

The first part of this template extends our previous file by replacing the block body section with additional functionality. In our case, this is a header for current tasks, then some repeating blocks for each task with buttons to edit and view.

Now that we have created our two templates, we need to update our application code to load the home screen on the root directory. We use the following code:

Python
from flask import Flask
from flask import render_template
 
app = Flask(__name__)
 
@app.route("/")
def home():
    return render_template(
        "home.html"
    )

There are three new lines here: first, we import Jinja’s render_template component, then we remove the text we had been returning and replace it with the render_template function, and finally, we specify the template file "home.html."

When we run our application now (F5), we should see our task list mockup on the screen.

When this is working, let’s deploy our application to our Azure Web App by switching to the Azure menu on the left, right-clicking our Web Application Instance, and selecting Deploy to Web App. When this process completes, we should be able to view the web app through the public URL.

Building a Detail Page and Edit Form

Let’s finish this front-end part of the web application by creating a view task and edit task template, and wiring up some more routes to those templates. First, we create a new file in our templates directory called "viewtask.html" and enter the following code:

HTML
{% extends "layout.html" %}
{% block body %}
<div class="container container-md px-4 py-5">
    <h2 class="pb-2 border-bottom">View Task</h2>
    <div class="row">
        <table class="table table-hover">
            <tr> <td>Name: </td><td>Example Task 01</td></tr>
            <tr> <td>Description: </td><td>This is just an example task with the number 01 as a placeholder</td></tr>
            <tr> <td>Due On: </td><td>2021 - 12 - 20</td> </tr>
            <tr> <td>Completed: </td><td>False</td> </tr>
            <tr> <td>Created On: </td><td>2021 - 11 - 11</td> </tr>
            <tr> <td>Modified On: </td><td>2021 - 11 - 11</td> </tr>
        </table>
    </div>
</div>
{% endblock %}

Like our task list view, viewtask.html is a standard HTML file with an example task to check the layout and data.

Next, we do the same thing for our edit screen. We create an "edittask.html" file in our templates directory with the following code:

HTML
{% extends "layout.html" %}
{% block body %}
<div class="container container-md px-4 py-5">
    <h2 class="pb-2 border-bottom">Edit Task</h2>
    <form>
        <div class="mb-3">
            <label for="taskName" class="form-label">Task Name</label>
            <input type="text" class="form-control" id="taskName">
        </div>
        <div class="mb-3">
            <label for="taskDescription" class="form-label">Description</label>
            <textarea  class="form-control" id="taskDescription" rows="5"></textarea>
        </div>
        <div class="mb-3">
            <label for="taskDue" class="form-label">Due On</label>
            <input type="text" class="form-control" id="taskDue">
        </div>
        <div class="form-check mb-3">
            <input type="checkbox" class="form-check-input" id="taskComplete">
            <label for="taskComplete" class="form-check-label">Completed</label>
        </div>
        <div class="container"><p></p></div>
        <div class="mb-3">
            <div class="row">
                <div class="col-md-4">
                    <label for="taskCreated" class="form-label">Created</label>
                    <input type="text" class="form-control" id="taskCreated" disabled>
                </div>
                <div class="col-md-4">
                    <label for="taskModified" class="form-label">Modified</label>
                    <input type="text" class="form-control" id="taskModified" disabled>
                </div>
            </div>  
        </div>
    </form>
</div>
{% endblock %}

This file is a little more complex because it implements some form controls, but it should work straightforwardly.

Finally, we need to construct some basic routes to these templates that we’ll build on when we hook up our data layer. We open the app.py file and add the following code to the bottom of the file:

Python
@app.route("/viewtask/<int:task_id>", methods=['GET'])
def view_task(task_id):
    return render_template(
        "viewtask.html"
    )
 
@app.route("/edittask/<int:task_id>", methods=['GET', ‘POST’])
def edit_task(task_id):
    return render_template(
        "edittask.html"
    )

These two routes are a little different from the home route. Depending on the route, they take in an integer called "task_id" and accept a GET or POST method, or both.

When these routes are complete, we can again test by running our code and going to the address http://127.0.0.1/view_task/(any_number). Once again, we can publish the code to our Azure Web App instance.

Next Steps

We should now have the bones of an application. We created it in Python, used the Flask server library, then deployed it to an Azure Web App service. We then tested our code locally before updating our cloud service with new functionality without leaving our VS Code instance.

We'll build on this code in the following article by connecting an Azure Cosmos DB database to save and retrieve application data.

To learn more about how deploy a Python web app to App Service on Linux, check out Quickstart: Create a Python app.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here