Tutorial: Run a Python (Django) web app with PostgreSQL in Azure App Service

Azure App Service provides a highly scalable, self-patching web hosting service. This tutorial shows how to connect a data-driven Python Django web app to an Azure Database for PostgreSQL database, and deploy and run the app on Azure App Service.

Python Django web app in Azure App Service

In this tutorial, you learn how to:

  • Create an Azure Database for PostgreSQL database and connect a web app to it
  • Deploy the web app to Azure App Service
  • View diagnostic logs
  • Manage the web app in the Azure portal

You can follow the steps in this article on macOS, Linux, or Windows. The steps are similar in most cases, although differences aren't detailed in this tutorial. Most of the examples below use a bash terminal window on Linux.

Prerequisites

Before you start this tutorial:

Test PostgreSQL installation and create a database

First, connect to your local PostgreSQL server and create a database:

In a local terminal window, run psql to connect to your local PostgreSQL server as the built-in postgres user.

sudo su - postgres
psql

or

psql -U postgres

If your connection is successful, your PostgreSQL database is running. If not, make sure that your local PostgresQL database is started by following the instructions for your operating system at Downloads - PostgreSQL Core Distribution.

Create a new database called pollsdb, and set up a database user named manager with password supersecretpass:

CREATE DATABASE pollsdb;
CREATE USER manager WITH PASSWORD 'supersecretpass';
GRANT ALL PRIVILEGES ON DATABASE pollsdb TO manager;

Type \q to exit the PostgreSQL client.

Create and run the local Python app

Next, set up and run the sample Python Django web app.

The djangoapp sample repository contains the data-driven Django polls app you get by following Writing your first Django app in the Django documentation.

Clone the sample app

In a terminal window, run the following commands to clone the sample app repository, and change to the new working directory:

git clone https://github.com/Azure-Samples/djangoapp.git
cd djangoapp

Configure the Python virtual environment

Create and activate a Python virtual environment to run your app.

python3 -m venv venv
source venv/bin/activate

or

py -3 -m venv venv
venv\scripts\activate

In the venv environment, run env.sh or env.ps1 to set the environment variables that azuresite/settings.py will use for the database connection settings.

source ./env.sh

or

.\env.ps1

Install the required packages from requirements.txt, run Django migrations, and create an admin user:

pip install -r requirements.txt
python manage.py migrate
python manage.py createsuperuser

Run the web app

After you create the admin user, run the Django server.

python manage.py runserver

When the Django web app is fully loaded, it returns something like the following message:

Performing system checks...

System check identified no issues (0 silenced).
December 13, 2019 - 10:54:59
Django version 2.1.2, using settings 'azuresite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Go to http://localhost:8000 in a browser. You should see the message No polls are available.

Go to http://localhost:8000/admin and sign in using the admin user you created in the last step. Select Add next to Questions, and create a poll question with some choices.

Run Python Django app in App Services locally

Go to http://localhost:8000 again to see the poll question and answer the question. The local Django sample application writes and stores user data to the local PostgreSQL database.

To stop the Django server, type Ctrl+C in the terminal.

Use Azure Cloud Shell

Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. You can use either Bash or PowerShell with Cloud Shell to work with Azure services. You can use the Cloud Shell preinstalled commands to run the code in this article without having to install anything on your local environment.

To start Azure Cloud Shell:

Option Example/Link
Select Try It in the upper-right corner of a code block. Selecting Try It doesn't automatically copy the code to Cloud Shell. Example of Try It for Azure Cloud Shell
Go to https://shell.azure.com, or select the Launch Cloud Shell button to open Cloud Shell in your browser. Launch Cloud Shell in a new window
Select the Cloud Shell button on the menu bar at the upper right in the Azure portal. Cloud Shell button in the Azure portal

To run the code in this article in Azure Cloud Shell:

  1. Start Cloud Shell.

  2. Select the Copy button on a code block to copy the code.

  3. Paste the code into the Cloud Shell session by selecting Ctrl+Shift+V on Windows and Linux or by selecting Cmd+Shift+V on macOS.

  4. Select Enter to run the code.

Most of the remaining steps in this article use Azure CLI commands in the Azure Cloud Shell.

Create and connect to Azure Database for PostgreSQL

In this section, you create an Azure Database for PostgreSQL server and database, and connect your web app to it. When you deploy your web app to Azure App Service, the app uses this cloud database.

Create a resource group

You can create a new resource group for your Azure Database for PostgreSQL server, or use an existing resource group.

A resource group is a logical container into which Azure resources like web apps, databases, and storage accounts are deployed and managed. For example, you can choose to delete the entire resource group in one simple step later.

In the Cloud Shell, create a resource group with the az group create command. The following example creates a resource group named myResourceGroup in the West Europe location. To see all supported locations for App Service on Linux in Basic tier, run the az appservice list-locations --sku B1 --linux-workers-enabled command.

az group create --name myResourceGroup --location "West Europe"

You generally create your resource group and the resources in a region near you.

When the command finishes, a JSON output shows you the resource group properties.

Create an Azure Database for PostgreSQL server

You create a PostgreSQL server with the az postgres server create command in the Cloud Shell.

Note

Before you create an Azure Database for PostgreSQL server, check which compute generation is available in your region. If your region doesn't support Gen4 hardware, change --sku-name in the following command line to a value that's supported in your region, such as Gen5.

In the following command, replace <postgresql-name> with a unique server name. The server name is part of your PostgreSQL endpoint https://<postgresql-name>.postgres.database.azure.com, so the name needs to be unique across all servers in Azure.

Replace <resourcegroup-name> and <region> with the name and region of the resource group you want to use. For <admin-username> and <admin-password>, create user credentials for the database administrator account. Remember the <admin-username> and <admin-password> to use later for signing in to the PostgreSQL server and databases.

az postgres server create --resource-group <resourcegroup-name> --name <postgresql-name> --location "<region>" --admin-user <admin-username> --admin-password <admin-password> --sku-name B_Gen5_1

When the Azure Database for PostgreSQL server is created, the Azure CLI returns JSON code like the following example:

{
  "administratorLogin": "myusername",
  "earliestRestoreDate": "2020-01-22T19:02:15.727000+00:00",
  "fullyQualifiedDomainName": "myservername.postgres.database.azure.com",
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.DBforPostgreSQL/servers/myservername",
  "location": "westeurope",
  "masterServerId": "",
  "name": "myservername",
  "replicaCapacity": 5,
  "replicationRole": "None",
  "resourceGroup": "myresourcegroup",
  "sku": {
    "capacity": 1,
    "family": "Gen5",
    "name": "B_Gen5_1",
    "size": null,
    "tier": "Basic"
  },
  < JSON data removed for brevity. >
}

Create firewall rules for the Azure Database for PostgreSQL server

Run the az postgres server firewall-rule create command to allow access to the database from Azure resources. Replace the <postgresql-name> and <resourcegroup-name> placeholders with your values.

az postgres server firewall-rule create --resource-group <resourcegroup-name> --server-name <postgresql-name> --start-ip-address=0.0.0.0 --end-ip-address=0.0.0.0 --name AllowAllAzureIPs

Note

The preceding setting allows network connections from all IP addresses within the Azure network. For production use, try to configure the most restrictive firewall rules possible by allowing only the outbound IP addresses your app uses.

Run the firewall-rule create command again to allow access from your local computer. Replace <your-ip-address> with your local IPv4 IP address. Replace the <postgresql-name> and <resourcegroup-name> placeholders with your own values.

az postgres server firewall-rule create --resource-group <resourcegroup-name> --server-name <postgresql-name> --start-ip-address=<your-ip-address> --end-ip-address=<your-ip-address> --name AllowLocalClient

Create and connect to the Azure Database for PostgreSQL database

Connect to your Azure Database for PostgreSQL server by running the following command. Use your own <postgresql-name> and <admin-username>, and sign in with the password you created.

psql -h <postgresql-name>.postgres.database.azure.com -U <admin-username>@<postgresql-name> postgres

Just as you did in your local PostgreSQL server, create a database and user in the Azure Database for PostgreSQL server:

CREATE DATABASE pollsdb;
CREATE USER manager WITH PASSWORD 'supersecretpass';
GRANT ALL PRIVILEGES ON DATABASE pollsdb TO manager;

Note

It's a best practice to create database users with restricted permissions for specific apps, instead of using the admin user. The manager user has full privileges to only the pollsdb database.

Type \q to exit the PostgreSQL client.

Test app connectivity to the Azure PostgreSQL database

Edit your local env.sh or env.ps1 file to point to to your cloud PostgreSQL database, by replacing <postgresql-name> with your Azure Database for PostgreSQL server name.

export DBHOST="<postgresql-name>.postgres.database.azure.com"
export DBUSER="manager@<postgresql-name>"
export DBNAME="pollsdb"
export DBPASS="supersecretpass"

or

$Env:DBHOST = "<postgresql-name>.postgres.database.azure.com"
$Env:DBUSER = "manager@<postgresql-name>"
$Env:DBNAME = "pollsdb"
$Env:DBPASS = "supersecretpass"

In the venv environment in your local terminal window, run the edited env.sh or env.ps1.

source ./env.sh

or

.\env.ps1

Run Django migration to the Azure database, and create an admin user.

python manage.py migrate
python manage.py createsuperuser

Once the admin user is created, run the Django server.

python manage.py runserver

In a browser, go to http://localhost:8000, and you should see the message No polls are available again.

Go to http://localhost:8000/admin, sign in using the admin user you created, and create a poll question like before.

Run Python Django app in App Services locally

Go to http://localhost:8000 again, and see the poll question displayed. Your app is now writing data to the Azure Database for PostgreSQL database.

To stop the Django server, type Ctrl+C in the terminal.

Deploy the web app to Azure App Service

In this step, you deploy the Azure Database for PostgreSQL database-connected Python app to Azure App Service.

Configure repository

Because this tutorial uses a Django sample, you need to change and add some settings in your djangoapp/azuresite/settings.py file to work with Azure App Service.

  1. Django validates the HTTP_HOST header in incoming requests. For your Django web app to work in App Service, you need to add the fully qualified domain name of the app to the allowed hosts.

    Edit azuresite/settings.py to change the ALLOWED_HOSTS line as follows:

    ALLOWED_HOSTS = [os.environ['WEBSITE_SITE_NAME'] + '.azurewebsites.net', '127.0.0.1'] if 'WEBSITE_SITE_NAME' in os.environ else []
    
  2. Django doesn't support serving static files in production. For this tutorial, you use WhiteNoise to enable serving the files. The WhiteNoise package was already installed with requirements.txt.

    To configure Django to use WhiteNoise, in azuresite/settings.py, find the MIDDLEWARE setting, and add whitenoise.middleware.WhiteNoiseMiddleware to the list, just after the django.middleware.security.SecurityMiddleware line. Your MIDDLEWARE setting should look like this:

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'whitenoise.middleware.WhiteNoiseMiddleware',
        ...
    ]
    
  3. At the end of azuresite/settings.py, add the following lines:

    STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    

    For more information about configuring WhiteNoise, see the WhiteNoise documentation.

Important

The database settings section already follows the security best practice of using environment variables. For complete deployment recommendations, see Django Documentation: deployment checklist.

Commit your changes into your fork of the djangoapp repository:

git commit -am "configure for App Service"

Configure a deployment user

FTP and local Git can deploy to an Azure web app by using a deployment user. Once you configure your deployment user, you can use it for all your Azure deployments. Your account-level deployment username and password are different from your Azure subscription credentials.

To configure the deployment user, run the az webapp deployment user set command in Azure Cloud Shell. Replace <username> and <password> with a deployment user username and password.

  • The username must be unique within Azure, and for local Git pushes, must not contain the ‘@’ symbol.
  • The password must be at least eight characters long, with two of the following three elements: letters, numbers, and symbols.
az webapp deployment user set --user-name <username> --password <password>

The JSON output shows the password as null. If you get a 'Conflict'. Details: 409 error, change the username. If you get a 'Bad Request'. Details: 400 error, use a stronger password.

Record your username and password to use to deploy your web apps.

Create App Service plan

In the Cloud Shell, create an App Service plan in the resource group with the az appservice plan create command.

The following example creates an App Service plan named myAppServicePlan in the Free pricing tier (--sku F1) and in a Linux container (--is-linux).

az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --sku F1 --is-linux

When the App Service plan has been created, the Azure CLI shows information similar to the following example:

{ 
  "adminSiteName": null,
  "appServicePlanName": "myAppServicePlan",
  "geoRegion": "West Europe",
  "hostingEnvironmentProfile": null,
  "id": "/subscriptions/0000-0000/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/myAppServicePlan",
  "kind": "linux",
  "location": "West Europe",
  "maximumNumberOfWorkers": 1,
  "name": "myAppServicePlan",
  < JSON data removed for brevity. >
  "targetWorkerSizeId": 0,
  "type": "Microsoft.Web/serverfarms",
  "workerTierName": null
} 

Create a web app

Create a web app in the myAppServicePlan App Service plan.

In the Cloud Shell, you can use the az webapp create command. In the following example, replace <app-name> with a globally unique app name (valid characters are a-z, 0-9, and -). The runtime is set to PYTHON|3.7. To see all supported runtimes, run az webapp list-runtimes --linux.

# Bash
az webapp create --resource-group myResourceGroup --plan myAppServicePlan --name <app-name> --runtime "PYTHON|3.7" --deployment-local-git
# PowerShell
az --% webapp create --resource-group myResourceGroup --plan myAppServicePlan --name <app-name> --runtime "PYTHON|3.7" --deployment-local-git

When the web app has been created, the Azure CLI shows output similar to the following example:

Local git is configured with url of 'https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git'
{
  "availabilityState": "Normal",
  "clientAffinityEnabled": true,
  "clientCertEnabled": false,
  "cloningInfo": null,
  "containerSize": 0,
  "dailyMemoryTimeQuota": 0,
  "defaultHostName": "<app-name>.azurewebsites.net",
  "deploymentLocalGitUrl": "https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git",
  "enabled": true,
  < JSON data removed for brevity. >
}

You’ve created an empty new web app, with git deployment enabled.

Note

The URL of the Git remote is shown in the deploymentLocalGitUrl property, with the format https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git. Save this URL as you need it later.

Configure environment variables

Earlier in the tutorial, you defined environment variables to connect to your PostgreSQL database.

In Azure App Service, you set environment variables as app settings, by using the az webapp config appsettings set command.

In the Azure Cloud Shell, run the following command to specify the database connection details as app settings. Replace <app-name>, <resourcegroup-name>, and <postgresql-name> with your own values.

az webapp config appsettings set --name <app-name> --resource-group <resourcegroup-name> --settings DBHOST="<postgresql-name>.postgres.database.azure.com" DBUSER="manager@<postgresql-name>" DBPASS="supersecretpass" DBNAME="pollsdb"

For information on how your code accesses these app settings, see Access environment variables.

Push to Azure from Git

Back in the local terminal window, add an Azure remote to your local Git repository. Replace <deploymentLocalGitUrl-from-create-step> with the URL of the Git remote that you saved from Create a web app.

git remote add azure <deploymentLocalGitUrl-from-create-step>

Push to the Azure remote to deploy your app with the following command. When Git Credential Manager prompts you for credentials, make sure you enter the credentials you created in Configure a deployment user, not the credentials you use to sign in to the Azure portal.

git push azure master

This command may take a few minutes to run. While running, it displays information similar to the following example:

Counting objects: 60, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (51/51), done.
Writing objects: 100% (60/60), 15.37 KiB | 749.00 KiB/s, done.
Total 60 (delta 9), reused 0 (delta 0)
remote: Deploy Async
remote: Updating branch 'master'.
remote: Updating submodules.
remote: Preparing deployment for commit id '06f3f7c0cb'.
remote: Repository path is /home/site/repository
remote: Running oryx build...
remote: Build orchestrated by Microsoft Oryx, https://github.com/Microsoft/Oryx
remote: You can report issues at https://github.com/Microsoft/Oryx/issues
. 
. 
. 
remote: Done in 100 sec(s).
remote: Running post deployment command(s)...
remote: Triggering recycle (preview mode disabled).
remote: Deployment successful.
remote: Deployment Logs : 'https://<app-name>.scm.azurewebsites.net/newui/jsonviewer?view_url=/api/deployments/06f3f7c0cb52ce3b4aff85c2b5099fbacb65ab94/log'
To https://<app-name>.scm.azurewebsites.net/<app-name>.git 
 * [new branch]      master -> master

The App Service deployment server sees requirements.txt in the repository root and runs Python package management automatically after git push.

Browse to the Azure app

Browse to the deployed app with URL http://<app-name>.azurewebsites.net. It takes some time to start, because the container must be downloaded and run when the app is requested for the first time. If the page times out or displays an error message, wait a few minutes and refresh the page.

You should see the poll questions that you created earlier.

App Service detects a Django project in your repository by looking for a wsgi.py file in each subdirectory, which manage.py startproject creates by default. When App Service finds the file, it loads the Django web app. For more information on how App Service loads Python apps, see Configure built-in Python image.

Go to http://<app-name>.azurewebsites.net/admin and sign in using the admin user you created. If you like, create some more poll questions.

Run Python Django app in App Services in Azure

Congratulations! You're running a Python (Django) web app in Azure App Service for Linux.

Stream diagnostic logs

You can access the console logs generated from inside the container. First, turn on container logging by running the following command in the Cloud Shell:

az webapp log config --name <app-name> --resource-group myResourceGroup --docker-container-logging filesystem

Once container logging is turned on, run the following command to see the log stream:

az webapp log tail --name <app-name> --resource-group myResourceGroup

If you don't see console logs immediately, check again in 30 seconds.

Note

You can also inspect the log files from the browser at https://<app-name>.scm.azurewebsites.net/api/logs/docker.

To stop log streaming at any time, type Ctrl+C.

Manage your app in the Azure portal

In the Azure portal, search for and select the app you created.

Navigate to your Python Django app in the Azure portal

By default, the portal shows your app's Overview page. This page gives you a view of how your app is doing. Here, you can also perform basic management tasks like browse, stop, restart, and delete. The tabs on the left side of the page show the different configuration pages you can open.

Manage your Python Django app in the Overview page in the Azure portal

Clean up resources

In the preceding steps, you created Azure resources in a resource group. If you don't expect to need these resources in the future, delete the resource group by running the following command in the Cloud Shell:

az group delete --name myResourceGroup

This command may take a minute to run.

Next steps

Go to the next tutorial to learn how to map a custom DNS name to your app:

Or check out other resources: