ICVOSS DJANGO PACKAGE REGISTRY

The package index django-hostmap Migrate from django-hosts

Migrate from django-hosts

Documentation

Goal

Move a project from django-hosts to django-hostmap. The migration is mechanical: translate ROOT_HOSTCONF entries into HOSTMAP, swap the middleware, then delete {% host_url %} usages at your own pace, since stock {% url %} now behaves correctly without them.

Prerequisites

Steps

1. Translate ROOT_HOSTCONF into HOSTMAP

django-hosts declares hosts as a list of (regex, urlconf, name) patterns in a separate hosts.py module. hostmap declares them as a dict of entries in settings directly.

# Before: hosts.py (django-hosts)
from django_hosts import host, patterns

host_patterns = patterns(
    "",
    host(r"www", "config.urls_www", name="www"),
    host(r"api", "config.urls_api", name="api"),
)
# After: settings.py (django-hostmap)
HOSTMAP = {
    "www": {"subdomain": "www", "urlconf": "config.urls_www"},
    "api": {"subdomain": "api", "urlconf": "config.urls_api"},
}
HOSTMAP_PARENT_DOMAIN = "example.com"  # was ROOT_HOSTCONF / PARENT_HOST
HOSTMAP_DEFAULT = "www"  # was DEFAULT_HOST

django-hosts regex patterns that only ever matched a literal label translate directly to a subdomain entry. A pattern using a genuine regex (variable capture groups, alternation) has no direct hostmap equivalent: hostmap supports exact hosts and single-level wildcards only (see use wildcard subdomains), not arbitrary regex. If your django-hosts patterns rely on regex capture beyond a single wildcard label, that is the one part of the migration that needs a design decision rather than a mechanical translation.

2. Swap the middleware

# Before
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "hosts.middleware.HostsRequestMiddleware",
    "django.middleware.common.CommonMiddleware",
    # ...
    "hosts.middleware.HostsResponseMiddleware",  # if used
]

# After
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "hostmap.middleware.HostmapMiddleware",
    "django.middleware.common.CommonMiddleware",
    # ...
]

hostmap has no response-side middleware to add; HostmapMiddleware does routing and reversing setup entirely on the request side, before get_response().

3. Update INSTALLED_APPS

INSTALLED_APPS = [
    # ...
    # "django_hosts",   # remove
    "hostmap",           # add
]

4. Leave {% host_url %} and django_hosts.reverse() in place for now

This is the point of the migration: you do not have to touch every template or Python call site before the project works. Once HOSTMAP and HostmapMiddleware are in place, stock {% url %} and reverse() are already host-aware everywhere, including inside third-party apps you never modified. {% host_url %} usages keep working as long as django_hosts itself is still installed and its own resolution still functions independently; once you are confident hostmap is routing correctly, delete {% host_url %} calls and swap them for {% url %} at whatever pace suits the codebase, since the explicit host= argument that django-hosts required is no longer needed anywhere hostmap's map already covers.

5. Remove django_hosts once every call site is converted

pip uninstall django-hosts

Then remove it from INSTALLED_APPS if it is still listed, and delete hosts.py.

Verify it worked

python manage.py check
python manage.py hostmap

Confirm the resolved map matches your old ROOT_HOSTCONF one-to-one, then spot-check the URLs that used to go through {% host_url %}:

import pytest
from django.urls import reverse


@pytest.mark.django_db
def test_migrated_host_resolves_the_same_as_before():
    assert reverse("api:user-detail", args=[7]) == "https://api.example.com/users/7/"

Common pitfalls