For a personal project (details forthcoming), I've been trying to have separate subdomains for accounts and allow some of these accounts to have CNAME support. So, if I have an account cwilliams, my data should be available at cwilliams.myproject.com. I could also add myproject.christopher-williams.net as a CNAME and get the same view.
The implementation is fairly straightforward. I ended up taking django-subdomains, removing some functionality that I didn't need and adding the CNAME handling.
To use this middleware:
- Put middleware.py (see below) somewhere in your project. I have a Django app called core for things like this.
- Add a CNAME field to a model of your choosing (a user profile might be appropriate), preferably as a CharField. URLField will not work.
- Add a SlugField to the same model and wire that up like you would any other slug.
- Add a new subdomain_urls.py and put the URLconf that should be used for your subdomains/CNAMEs.
- Add SUBDOMAIN_URLCONF='yourproject.subdomain_urls' to settings.py.
- Add core.middleware.SubdomainMiddleware to MIDDLEWARE_CLASSES in settings.py (bottom of the list should work for most usages).
- Create some fake DNS entries in your hosts file (see below).
- Set up your slug/CNAME entries created in your model (i.e. for slugs, test1, test2. For CNAMEs, djangotest.christopher-williams.net, etc.)
- Open your fake subdomains in your browser.
Any hostname that doesn't have a corresponding slug/CNAME will be routed through your ROOT_URLCONF. Otherwise, you should see what's in your SUBDOMAIN_URLCONF.
[...] # Fake DNS entries for testing the subdomain middleware 127.0.0.1 test1.django.local 127.0.0.1 test2.django.local 127.0.0.1 djangotest.christopher-williams.net 127.0.0.1 django.local
from django.utils.cache import patch_vary_headers from django.conf import settings from django.db.models import Q from account.models import Account class SubdomainMiddleware: def process_request(self, request): # Strip off the port, mostly useful for development environments fqdn = request.get_host().split(':') # Break up the domain into parts and get the subdomain slug domain_parts = fqdn.split('.') if len(domain_parts) > 2: subdomain = domain_parts if subdomain.lower() == 'www': subdomain = None else: subdomain = None try: request.account = Account.objects.get( Q(cname=fqdn) | Q(slug=subdomain) ) except Account.DoesNotExist: pass else: request.urlconf = settings.SUBDOMAIN_URLCONF def process_response(self, request, response): patch_vary_headers(response, ('Host',)) return response