Configuring and setting up subdomains on Django using django-hosts

Configuring and setting up subdomains on Django using django-hosts

A subdomain is basically a domain that’s part of another main domain and it is used a lot in many websites. I spent a decent amount of time figuring out how to do that and found this very helpful post and decided to extend it a little in a tutorial form. So if you ever want to add a subdomain for your Django site, something like subdomain.your-site.com, this might help.

We are going to go through all the steps from creating a project in django, to getting our subdomain up and running.

Create a Python virtual environment

As a best practice, we will create a virtual environment. I will use conda here to create it with a specific version of python.

$ conda create -n django-env python=3.6
$ conda activate django-env

Packages installation

Now let’s use pip to install Django [3] and django-hosts [4]:

$ pip install django django-hosts

Create a Django project and app

Once installed, let’s go to our workspace directory and create our django project:

$ django-admin startproject simpletest

You will see a new directory created named simpletest and inside it, a manage.py file and another simpletest folder. To avoid confusion between the project and our apps, I like to rename the parent folder so l’ll just call it simpletest-prj. Now you will have something like this:

simpletest-prj
├── manage.py
└── simpletest
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

Let’s move inside our simpletest directory to run python3 manage.py runserver and open http://localhost:8000/ to make sure our project works. You should get a page like this.

Now stop your application and create a new app that we will call home:

$ python3 manage.py startapp home

We now want to put our own homepage instead of the django default landing page by following a few steps:

First we need to add a new line to the urls.py file in simpletest under the admin url so that Django knows where to look and redirect when opening our site:

from django.contrib import admin
from django.urls import path
from . import views  # <-- new import

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.home),  # <-- new line
]

Then we need to create a views.py file under the simpletest folder with the following code. This is the one with the logic, but now we will only render and return a home.html file:

from django.shortcuts import render

def home(request):
    return render(request, 'home.html')

The next thing is to create a template folder under the main project directory simpletest-prj and our home.html file with the following content:

<html>
    <head>
        <title>Welcome!</title>
    </head>
    <body>
        Hello there, and welcome to my new site.
    </body>
</html>

Finally, we need to create our TEMPLATE_DIR variable and add it to the TEMPLATES structure on the settings.py file:

import os

# Right below the BASE_DIR declaration
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates')
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [TEMPLATE_DIR,],  # <-- This is the modified line
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Refresh your page or run it one more time to get the expected result:

Our directory structure will now look like this:

simpletest-prj
├── db.sqlite3
├── home
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── manage.py
├── simpletest
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   ├── views.py
│   └── wsgi.py
└── templates
    └── home.html

Configure the app with subdomain

So now our main page is located at localhost:8000 and what we like to do is to host our home app on home.localhost:8000 instead of something like localhost:8000/home. For that we will do the following:

We need to add home to our app list along with the already installed django-hosts in the settings.py file:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'home',  # <-- Add this
    'django_hosts',  # <-- and this
]

Then let’s add our hosts to the ALLOWED_HOSTS variable in the same file:

ALLOWED_HOSTS = ['localhost', 'home.localhost']

The MIDDLEWARE variable needs to be updated with the following two values, the first one at the beginning and the second one at the end

'django_hosts.middleware.HostsRequestMiddleware'
'django_hosts.middleware.HostsResponseMiddleware'
MIDDLEWARE = [
    'django_hosts.middleware.HostsRequestMiddleware',  # <-- new line
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django_hosts.middleware.HostsResponseMiddleware',  # <-- another new line
]

Under the ROOT_URLCONF line, add the following:

ROOT_HOSTCONF = 'simpletest.hosts'
DEFAULT_HOST = 'www'

And now we create a hosts.py file under simpletest. This file will contain all the list of subdomains that will be on our site:

from django_hosts import patterns, host
from django.conf import settings

host_patterns = patterns(
    '',
    host(r'www', settings.ROOT_URLCONF, name='www'),
    host(r'home', 'home.urls', name='home')
)

So now we go on to create and edit a urls.py file under the home directory:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
]

And create our view in its corresponding views.py file:

from django.shortcuts import render

def home(request):
    return render(request, 'home/hello.html')

Create a templates folder under home with another home subdirectory and a hello.html file inside:

<html>
    <head>
        <title>Home</title>
    </head>
    <body>
        Welcome to the subdomain homepage!
    </body>
</html>

Save all, refresh your application or run it one more time, and now go to http://home.localhost:8000/

For reference, the directory structure should look something like the following at this point:

simpletest-prj
├── db.sqlite3
├── home
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── templates
│   │   └── home
│   │       └── hello.html
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── manage.py
├── simpletest
│   ├── __init__.py
│   ├── asgi.py
│   ├── hosts.py
│   ├── settings.py
│   ├── urls.py
│   ├── views.py
│   └── wsgi.py
└── templates
    └── home.html

And that would be it! When you’re ready to deploy your application on your configured domain, just make sure you edit the ALLOWED_HOSTS variable to match the name of the actual domain.