`); }

Django app配置子域名

创建于 访问量:

最近由于PM的需求,又去研究了Django一个app对应一个子域名的用法。寻找资料的过程中发现没有较好的中文教程,遂有此篇。

安装Django-Hosts

按照惯例,首先需要pipenv install django-hosts(如果你没有pipenv, pip也是一样的)。

  1. 'django_hosts'加入项目设置中的INSTALLED_APPS

  2. 'django_hosts.middleware.HostsRequestMiddleware' 加到项目设置中的MIDDLEWARE的第一行。

    'django_hosts.middleware.HostsResponseMiddleware' 加到项目设置中的MIDDLEWARE的最后一行。

    完成之后你的MIDDLEWARE配置看起来就像这样

    MIDDLEWARE = [
        'django_hosts.middleware.HostsRequestMiddleware',
        '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',
    ]
    
  3. 在项目settings.py的同目录下创建一个hosts.py

  4. settings.py里增加三个变量

    1. ROOT_HOSTCONF = 'mysite.hosts'

      它指向你上一步创建的hosts.py

    2. DEFAULT_HOST

      当没有匹配到对应的子域名时,会默认使用这个子域名。一般来说,你可以设置为'www'

    3. PARENT_HOST

      要附加到反向域的父域名。(也许你现在搞不懂它有什么用,往下看)

编写规则

hosts.py文件里写入如下的代码

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

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

然后在ALLOWED_HOSTS里加入它们, 例如 ['www.abersheeran.com', 'api.abersheeran.com']。要注意,像['*.abersheeran.com']这样的通配符是没用的。

当一个请求到达时,会最先被'django_hosts.middleware.HostsRequestMiddleware'获取,然后根据hosts里的配置匹配到对应的根urls里。

至于各个app里的urls都直接按照Django正常的写法就好。

模板编写以及其他

Django的url最让人舒服的一点就是可以通过唯一的名称空间 + URL名称映射到具体的url,使得产品经理要求更改url路径时,不需要那么麻烦。

在使用Django-Hosts之后,这种映射依旧存在(hosts.py中每个host都要有name),但需要修改一下模板的书写规则

{% load hosts %}
<a href="{% host_url 'homepage' host 'www' %}">Home</a> |

或者,如果你需要在Python代码里获取到对应的url, 那么可以这么写

from django.shortcuts import render
from django_hosts.resolvers import reverse

def homepage(request):
    homepage_url = reverse('homepage', host='www')
    return render(request, 'homepage.html', {'homepage_url': homepage_url})

这些反向映射的url,都会变成 host + PARENT_HOST 的域名形式。例如{% host_url 'homepage' host 'www' %}这个会被映射到www.PARENT_HOST。如果你没有设置PARENT_HOST,它将被映射到类似于http://www/home之类的url上。

还有更多的用法,都在本文第一个链接所给的官方文档中。没有多少英文,更多的是代码,可以尝试自己读一读。

一个样例

对于有很多个子域名站点来说,一个个进行手动注册管理,就很麻烦了。于是我对稍微利用了一下Django的规则来自动注册。

首先修改hosts.py如下。

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

host_patterns = patterns(
    '',
    *[host(subdomain, subdomain+'.urls', name=subdomain) for subdomain in settings.SUBDOMAINS]
)

再修改settings.py中的部分配置

ROOT_DOMAIN = '.abersheeran.com'

# Django-Host settings
# https://django-hosts.readthedocs.io/en/latest/index.html

ROOT_HOSTCONF = 'mysite.hosts'

DEFAULT_HOST = 'www'

ROOT_URLCONF = 'www.urls'

# 子域名对应的app名
# 例如 api.abersheeran.com 对应的app为 api
SUBDOMAINS = [
    'www',
    'api',
]

ALLOWED_HOSTS = [domain + '.localhost' if DEBUG else ROOT_DOMAIN for domain in SUBDOMAINS]

# Application definition

INSTALLED_APPS = SUBDOMAINS + [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_hosts',  # 多域名
]

每个子域名都对应自己的同名app,譬如www这个app对应的就是www.abersheeran.com

当开启DEBUG时,自动将各个子域名挂到localhost上,譬如访问api.localhost就可以进行api这个app 的测试。关闭DEBUG时,默认是部署到线上环境了,就自动将测试使用的localhost替换成真正使用的域名。

题外话

如何进行本地测试

例如访问api.example.com。你只需要修改本机的hosts文件,把api.example.com映射到127.0.0.1即可。对于不同的系统,hosts文件存在的路径不同,Linux大多在/etc/hosts,Windows10则是在C:\Windows\System32\drivers\etc\hosts。当然,你也可以为了这么点小事下个第三方工具来用,不过它们也都是修改hosts文件来做到这一点,只是封装了一下界面而已。

最佳的方案还是使用诸如api.localhostlocalhost的子域名,因为它们默认就是访问本地的服务器(至少经我实验, Windows10上如此)