Configuring and setting up subdomains on Django using django-hosts

Miguel ÁngelMiguel Ángel
5 min read

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.

19
Subscribe to my newsletter

Read articles from Miguel Ángel directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Miguel Ángel
Miguel Ángel

Software Engineer that works mainly with Go and Python. Loves music and beer.