最近由于PM的需求,又去研究了Django一个app对应一个子域名的用法。寻找资料的过程中发现没有较好的中文教程,遂有此篇。
安装Django-Hosts
按照惯例,首先需要pipenv install django-hosts
(如果你没有pipenv, pip也是一样的)。
-
把
'django_hosts'
加入项目设置中的INSTALLED_APPS
-
把
'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', ]
-
在项目
settings.py
的同目录下创建一个hosts.py
。 -
在
settings.py
里增加三个变量-
ROOT_HOSTCONF = 'mysite.hosts'
它指向你上一步创建的
hosts.py
-
DEFAULT_HOST
当没有匹配到对应的子域名时,会默认使用这个子域名。一般来说,你可以设置为
'www'
-
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.localhost
的localhost
的子域名,因为它们默认就是访问本地的服务器(至少经我实验, Windows10上如此)