Deploying a Django application in Windows with Apache and mod_wsgi

Django is a powerful web framework widely used by many famous applications. Django includes a simplified development server for testing your code locally, but for anything even slightly production related, a more secure and powerful web server is required.

In this guide, we will demonstrate how to install and configure some components on a Windows server to support and serve Django applications. We will be setting up a MySQL database instead of using the default SQLite database. We will configure mod_wsgi to interface with our applications, and set up Apache to act as the gateway to the world.

Python

The first step is to download and install Python from the Python website. Select Windows as operating system and pick the 32 / 64 bit version based on your machine requirements.

Once you download the setup file, right click the setup file and select Run as Administrator. You should see the below screen

Python Installation

Make sure both the checboxes at the bottom of the window are checked (Install for all users, and Add Python 3.7 to PATH checkboxes)

Click on Install Now. Once the installation is completed, you can close the window. To make sure the installation is successful, open Powershell and type python. If the installation was successful, you should see an output similar to below

Python Shell

MySQL

The next step is the installation of MySQL. Before installation of MySQL, Visual Studio and the Visual C++ libraries have to be installed.

Download Visual Studio 2017 from here. When installing Visual Studio, make sure you select Desktop Environment with C++ on Workloads section, and the C++/CLI Support option on the right side of the screen.

Visual Studio 2017 Installation

Download the latest Visual C++ from here and install it. Download the latest version of MySQL. Run the downloaded setup file. The first screen should look like this:

MySQL step 1

Click on I accept the license terms and click Next

In the type of installation, choose Developer Default

MySQL - Type of Installation

In the requirements section, click Next. Then click on execute. Once all the items in the list are installed, you will then be taken to the configuration pages. In the Type and Networking page, choose Config Type as Server Computer

MySQL type and networking

In the next screen, setup the root user's password. And in the users section, add a user with the name as same as the logged in user. Click on Next till the end and click Exceute at last. In the next 2 configuration steps, nothing needs to be changed and so you can click on Next/Execute.

Once the installation is complete, create a new database called my_application and provide all privileges of the same to the new user created.

mysql> create database my_application;
Query OK, 1 row affected (0.04 sec)

mysql> grant all on my_application.* to '<my-user>'@'localhost';
Query OK, 0 rows affected (0.08 sec)

virtualenv

A virtual environment is a tool that helps to keep dependencies required by different projects separate by creating isolated python virtual environments for them. To install virtualenv, open powershell and run

pip install virtualenvwrapper-win

This will install the virtualenv package. In order to create a virtual environment and to start working on the virtual environment, run

mkvirtualenv my_application
workon my_application

Install Django

Install Django and mysql connector using pip with the following command

pip install django
pip install pymysql

Create Django application

Next, let's create a sample Django project. Type the following command in the powershell window.

django-admin startproject my_application

Once that command is executed, you should see a folder called my_application in the current folder and the my_application will have the following structure

my_application
|   manage.py
|
\---my_application
        settings.py
        urls.py
        wsgi.py
        __init__.py

Now let's run the server and check that it can be accessed from the browser

cd my_application
python manage.py runserver

Once the server starts running, you should see a similar output in your powershell terminal

PS C:\Users\myuser\my_application> python .\manage.py runserver
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.
April 29, 2019 - 06:15:40
Django version 2.1.5, using settings 'my_application.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Now let's access the server from the browser. Go to the URL - http://127.0.0.1:8000/ from your browser and you should see the following screen

Default Django page

Now you can stop the server by pressing Ctrl + C in the powershell terminal.

Local setup

Next step is to provide the appropriate DB settings, e.t.c for the Django application. Open your my_application/settings.py file and replace the DATABASES variable to the following:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'my_application',
        'USER': '<my-user>',
        'PASSWORD': '<my-password>',
        'HOST': 'localhost',
        'PORT': 3306
    }
}

In production environment, Django does not serve static files (css, js and images). Inorder for them to be server properly by Apache, let's configure the staticfile settings in the application. Add the following at the end of your my_application/settings.py file

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

Once the details are entered, create the database tables, and collect the staticfiles by running

python manage.py makemigrations
python manage.py migrate
python manage.py collectstatic

After collectstatic, you should see a static folder created in your base folder. The current structure will be this

my_application
+---my_application
|   \---__pycache__
\---static
    \---admin
        +---css
        |   \---vendor
        |       \---select2
        +---fonts
        +---img
        |   \---gis
        \---js
            +---admin
            \---vendor
                +---jquery
                +---select2
                |   \---i18n
                \---xregexp

Apache

Apache is the webserver that will be handling web requests, and communicating to the Django app using mod_wsgi. Download WAMP server from here. The default installation should have created a folder in C:, as C:\wamp64

Once you verify that the above folder is created, add an Environment variable in your machine with name MOD_WSGI_APACHE_ROOTDIR whose value should be C:\wamp64\bin\apache\apache<version>\.

You will need to restart the powershell window for this change to take effect.

wsgi.py

The default wsgi.py generated by Django works properly for LINUX based deployments, but for Windows we need to make a few changes to the file. Create a new file my_application/wsgi_windows.py and paste the following contents in the file:

activate_this = 'C:/Users/myuser/Envs/my_application/Scripts/activate_this.py'
# execfile(activate_this, dict(__file__=activate_this))
exec(open(activate_this).read(),dict(__file__=activate_this))

import os
import sys
import site

# Add the site-packages of the chosen virtualenv to work with
site.addsitedir('C:/Users/myuser/Envs/my_application/Lib/site-packages')




# Add the app's directory to the PYTHONPATH
sys.path.append('C:/Users/myuser/my_application')
sys.path.append('C:/Users/myuser/my_application/my_application')

os.environ['DJANGO_SETTINGS_MODULE'] = 'my_application.settings'
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_application.settings")

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

mod_wsgi

Once WAMP is installed, go to the my_application folder in powershell and install mod_wsgi by running

pip install mod_wsgi

To get the Apache configration to be set related to this project, run

mod_wsgi-express module-config

The output should be something similar to:

LoadFile "c:/users/myuser/appdata/local/programs/python/python37/python37.dll"
LoadModule wsgi_module "c:/users/myuser/appdata/local/programs/python/python37/lib/site-packages/mod_wsgi/serve
r/mod_wsgi.cp37-win_amd64.pyd"
WSGIPythonHome "c:/users/myuser/appdata/local/programs/python/python37"

Copy the output generated by the mod_wsgi-express command and paste it at the end of C:\wamp64\bin\apache\apache<version>\conf\httpd.conf.

Next step is to open the vhosts file at C:\wamp64\bin\apache\apache<version>\conf\extra\httpd_vhosts.conf and replace the contents over there with the content below:

# virtual SupervisionTool
<VirtualHost *:80>
    ServerName localhost 
    WSGIPassAuthorization On
    ErrorLog "logs/my_application.error.log"
    CustomLog "logs/my_application.access.log" combined
    WSGIScriptAlias /  "C:\Users\myuser\my_application\my_application\wsgi_windows.py"
    <Directory "C:\Users\myuser\my_application\my_application">
        <Files wsgi_windows.py>
            Require all granted
        </Files>
    </Directory>

    Alias /static "C:/Users/myuser/my_application/static"
    <Directory "C:/Users/myuser/my_application/static">
        Require all granted
    </Directory>  
</VirtualHost>
# end virtual SupervisionTool

With this setup, you should now be able to go to the System Services panel, start the Apache service and you should be able to access the application at http://localhost