在上一章,为其他程序与我们的Web应用交互创建了RESTful API。本章将学习如何创建生产环境让我们的网站正式上线,主要内容有:
配置生产环境
创建自定义中间件
实现自定义管理命令
1创建生产环境
现在该将Django项目正式部署到生产环境中了。我们将按照下列步骤将站点部署到生产环境中:
为生产环境配置项目设置
使用PostgreSQL数据库
使用uWSGI和NGINX建立web服务器
管理静态资源
使用SSL加强站点安全管理
1.1管理用于多个环境的配置
在实际的项目中,很可能要面对不同的环境。一般至少有一个本地开发环境和一个生产环境,也可能有其他环境比如测试环境,预上线环境等。对于不同的环境,有些设置是通用的,有些则因环境而异。让我们将项目设置为可以适合不同环境,又可以保证项目结构不会被改变。
在educa/educa/目录下建立settings目录(包),与settings.py同级,将settings.py文件重命名为base.py然后移动到settings目录中来,再创建其他文件,setting/目录如下所示:
Copysettings/
__init__.py
base.py
local.py
pro.py
这些文件用途如下:
base.py:基本的设置文件,包含通用的设置,是原来的settings.py
local.py:本地环境的自定义设置
pro.py:生产环境的自定义设置
编辑settings/base.py,找到下列这行:
CopyBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
将其替换成下边这行:
CopyBASE_DIR =
os.path.dirname(os.path.dirname(os.path.abspath(os.path.join(__file__, os.pardir))))
由于我们将settings.py文件又往下级目录放了一级,必须让BASE_DIR指向正确的路径,所以使用了os.pardir指向父目录,来让最后的路径依然是原来的项目根目录。
编辑settings/local.py,添加下列代码:
Copyfrom .base import *
DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
这是代表我们本地环境的配置文件。在其中导入了所有base.py中的设置内容,然后写了DEBUG和DATABASES两个设置,这两个设置会覆盖原来base.py中的设置,成为本文件中的设置。由于DEBUG设置和DATABASES设置在每个配置文件中都会修改,也可以将这两个设置从base.py中删除。
再来编辑settings/pro.py,如下所示:
Copyfrom .base import *
DEBUG = False
ADMINS = (
('Antonio M', 'email@mydomain.com'),
)
ALLOWED_HOSTS = ['*']
DATABASES = {
'default': {
}
}
这是生产环境的配置文件,来详细看一下其中的内容:
DEBUG:设置DEBUG为False是生产环境的强制要求。如果不关闭,会将错误跟踪和敏感配置信息泄露给所有人。
ADMINS:当DEBUG设置为False的时候,如果一个视图抛出异常,所有信息会以邮件形式发送到ADMINS配置中列出的所有人。需要将其中的信息改成自己的名字和邮箱(还需要配置SMTP服务器)。
ALLOWED_HOSTS:Django只会向这个设置中的地址或者主机名称提供Web服务。这是一个安全手段。我们使用了通配符*表示可以用于所有主机名称或者IP地址。在稍后的配置中会更详细的作出限制。
DATABASES:生产环境的数据库设置,现在留空,后边会进行该设置。由于生产环境的数据库和非生产环境的数据库一般是隔离的,甚至生产环境数据库只有处于生产环境才能访问。所以该项需要单独配置。
在需要面对多种环境时,建立一个基础配置文件并为每种环境编写单独的配置文件。用于具体环境的配置文件继承基础配置并重写与环境相关的配置即可。
由于我们现在没有把配置文件放在原来settings.py所在的位置,所以无法运行manage.py,必须为其指定settings模块的所在路径,即使用--settings参数或者设置环境变量DJANGO_SETTINGS_MODULE。
打开系统命令行窗口输入:
Copyexport DJANGO_SETTINGS_MODULE=educa.settings.pro
这条命令会为当前的会话窗口设置DJANGO_SETTINGS_MODULE环境变量。如果不想每次运行shell都执行一遍,可以把这条命令加入到shell配置文件如.bashrc或者.bash_profile中。
如果不想对系统进行任何设置,那么在启动站点的时候必须加上--settings参数,如下:
Copypython manage.py migrate --settings=educa.settings.pro
现在我们就为多环境做好了基础设置。
1.2使用PostgreSQL数据库
在整本书中,我们大部分都使用了Python自带的SQLite数据库,只要在博客全文检索的时候推荐使用了PostgreSQL数据库。SQLite轻量而且易于使用,但对于生产环境而言太过简陋,必须需要一个更强力的数据库比如PostgreSQL和MySQL或者Oracle。PostgreSQL的安装在第三章中已经介绍过,不再赘述。
让我们为我们的应用创建一个PostgreSQL用户,打开系统命令行输入如下命令:
Copysu postgres
createuser -dP educa
系统会提示输入用户密码和权限。输入密码并且给予用户权限,然后使用下列命令建立一个新的数据库:
Copycreatedb -E utf8 -U educa educa
这样就建立好了一个新的数据库并且将其分配给educa用户,之后编辑settings/pro.py,修改数据库的设置如下:
CopyDATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'educa',
'USER': 'educa',
'PASSWORD': '',
}
}
将密码部分替换成为educa用户设置的密码。由于新数据库是空的,运行:
Copypython manage.py migrate
然后创建一个超级用户:
Copypython manage.py createsuperuser
译者注,安装PostgreSQL远没有这么简单,尤其是通过第三方程序远程管理PostgreSQL,需要修改PostgreSQL的配置文件,将认证方式修改为md5或者trust,然后启用允许访问的IP,建议查看官方文档和各种安装教程进行配置。
1.3部署前检查
Django提供了一个check命令,可以在任何时候检查项目。通常检查过程包括检查所有注册的应用,输出所有错误和警告信息。如果包含--deploy参数,还会额外执行针对生产环境的检查。
打开系统终端然后输入如下命令进行检查:
Copypython manage.py check --deploy
译者注:作者这里遗漏了配置文件的路径,应该写成python manage.py check --deploy --settings=educa.settings.*,其中*为base,local或pro
如果站点编写正确的话,会看到没有错误输出,但是会有一些警告信息。这说明站点通过了检查,但这些警告信息应该得到处理,以让站点更加安全。本书不会深入这里的内容,但是要记得在正式部署之前一定要进行部署前检查。
1.4通过WSGI程序提供Django服务
Django的主要部署平台就是WSGI,WSGI是Web Server Gateway Interface的简称,是基于Python的程序提供Web服务的标准格式。由于Django也是Python程序,也需要通过WSGI对外提供服务。
当通过startproject命令新建一个项目的时候,Django会在项目目录内新建一个wsgi.py。这个文件包含了一个WSGI可调用函数,为我们的Django应用提供了一个接口。无论是我们之前采用本机8000端口的开发服务器,还是正式生产环境,都需要通过这个接口。关于WSGI的详细知识可以看https://wsgi.readthedocs.io/en/latest/及Python的PEP333。
1.5安装WSGI
直到本节之前,我们的所有开发都是在django在本地环境运行的开发服务器上进行的。在生产环境中,需要一个真正的web服务器才能部署django服务。
uWSGI是一个非常快的Python应用程序WSGI服务器,使用WSGI标准与Python应用进行通信。uWSGI把HTTP请求翻译成Django程序能够处理的格式。
安装uSWGI:
Copypip install uwsgi==2.0.17
在pip安装之后,会built uWSGI(编译安装),需要一个C编译器,比如GCC或者clang,在linux环境下可以输入命令:apt-get install build-essential。
如果是MacOS X,可以通过Homebrew安装,执行命令:brew install uwsgi。如果在windows下安装,需要Cygwin https://www.cygwin.com
。推荐在基于UNIX的操作系统上安装uWSGI。
UNIX环境下如果看到Successfully built uwsgi就说明成功安装了uWSGI。关于uWSGI的文档可以在https://uwsgi-docs.readthedocs.io/en/latest/找到。
1.6配置uWSGI
可以通过命令行配置uWSGI,打开系统命令行模式,进入educa项目的根目录,然后输入:
Copysudo uwsgi --module=educa.wsgi:application --env=DJANGO_SETTINGS_MODULE=educa.settings.pro --master --pidfile=/tmp/project-master.pid --http=127.0.0.1:8000 --uid=1000 --virtualenv=/home/env/educa/
必须需要su权限才可以。通过这条命令,为本机上的uWSGI设置了如下的内容:
使用educa.wsgi:application作为调用接口
载入生产环境的设置文件
使用virtualenv设置的虚拟环境,注意将/home/env/educa/替换为实际的虚拟环境所在路径。如果未使用虚拟环境,该配置可以不填。
如果不是在项目目录内执行的上述命令,需要额外加一个参数指定具体的项目目录--chdir=/path/to/educa/,将其中的/path/to/educa/替换成educa的项目路径。
通过浏览器访问http://127.0.0.1:8000/(无需启动django服务),可以看到站点内容显示了出来,但没有任何CSS样式,也无法显示图片,这是因为还没有配置uWSGI来提供静态文件服务。
uWSGI允许使用一个.ini配置文件进行自定义配置,比使用命令行要方便很多。在educa项目根目录下建立:
Copyconfig/
uwsgi.ini
编辑uwsgi.ini,添加如下代码:
Copy[uwsgi]# variablesprojectname = educa
base = /home/projects/educa
# configurationmaster = truevirtualenv = /home/env/%(projectname)
pythonpath = %(base)
chdir = %(base)
env = DJANGO_SETTINGS_MODULE=%(projectname).settings.pro
module = educa.wsgi:application
socket = /tmp/%(projectname).sock
在这个.ini文件里我们定义了两个变量:
projectname:Django项目的名称,是educa
base:educa项目的绝对路径,将其替换成实际项目路径
上边定义的这两个变量是自定义变量,还可以定义任意其他变量,只要不和内置的名称冲突。接下来是具体设置的解释:
master:表示启用主进程
virtualenv:虚拟环境地址,将其替换成实际的路径所在(不包含bin/activate)
pythonpath:加入到Python PATH中的地址,一般就是项目的根目录
chdir:项目的实际地址,uWSGI会在加载应用之前将工作目录变更到这个路径
env:环境变量,设置为DJANGO_SETTINGS_MODULE,具体路径指向生产环境的配置文件
module:要使用的WSGI模块,指向项目中的wsgi.py中的调用函数。application是该函数在项目中默认的命名。
socket:绑定该服务的套接字。(是一个文件套接字,用于与NGINX通信)
其中的socket套接字是用于和第三方路由软件进行通信,比如NGINX。命令行模式中我们使用的--http 127.0.0.1:8000指的是让uWSGI自己接受HTTP请求并自己负责路由这些请求。我们需要把uWSGI作为socket启动(在.ini文件设置中并没有设置--http参数),因为我们要使用NGINX作为我们的web服务器,NGINX通过刚才设置的文件套接字与uWSGI进行通信。
关于uWSGI的详细设置可以看 https://uwsgi-docs.readthedocs.io/en/latest/Options.html。
现在可以通过使用配置文件来启动uWSGI(先关闭原来运行的uWSGI服务):
Copyuwsgi --ini config/uwsgi.ini
这样运行之后,可以发现暂时无法通过浏览器访问http://127.0.0.1:8000/,因为此时uWSGI监听文件套接字而不是HTTP端口,我们还需要继续完善生产环境配置。
1.7配置uWSGI
当启动一个Web服务的时候,很显然必须提供动态的内容服务,但也需要静态的文件服务,比如CSS,JavaScript文件,图像等。如果用uWSGI来管理静态文件,会为HTTP请求增加不必要的开销,所以最好在uWSGI之前加一个Web服务,比如NGINX。
NGINX是一个高并发,低内存占用的Web服务端,也具有反向代理功能,即接受一个HTTP请求,然后把这个请求路由给不同的后端。通常来说,你需要一个web服务端如NGINX,用于快速高效的提供静态文件,然后把动态的请求转发给uWSGI。通过使用NGINX,还可以设置其反向代理功能从而更好的提供web服务。
安装NGINX可以使用下列命令:
Copysudo apt-get install nginx
如果使用MacOS X,可以通过brew install nginx来安装。Windows下的NGINX可以通过https://nginx.org/en/download.html下载。
译者注:安装NGINX后不会立刻启动,译者使用的Centos 7.5 1804还需要启动NGINX服务和开机启动:
Copysystemctl start nginx.service
systemctl enable nginx.service
正常情况下在启动NGINX之后,直接访问本机IP地址,可以看到NGINX欢迎页面,表示基础配置成功运行,之后可以先停用NGINX服务,以配置生产环境。
1.8生产环境
下面的图表示了我们最终配置的生产环境的结构:
当一个浏览器发起一个HTTP请求的时候,发生如下事情:
NGINX接收HTTP请求
如果请求静态文件,NGINX直接提供服务。如果请求动态页面,NGINX通过SOCKET与uWSGI通信,将请求转交给uWSGI处理
uWSGI将请求转交给Django后端进行处理,返回的响应被传递给NGINX,NGINX再发回给浏览器。
1.9配置NGINX
在config/目录下创建nginx.conf文件,在其中添加如下代码:
Copy# the upstream component nginx needs to connect to
upstream educa {
server unix:///tmp/educa.sock;
}
server {
listen80;
server_name www.educaproject.com educaproject.com;
location / {
include /etc/nginx/uwsgi_params;
uwsgi_pass educa;
}
}
这是NGINX的基础配置。我们建立了一个upstream名叫educa,指定了uWSGI使用的socket名称 ,然后使用server指令,其中的设置有:
listen 80表示让NGINX监听80端口
设置主机名为www.educaproject.com和educaproject.com,NGINX会为这两个主机地址提供服务
配置location参数,将所有在'/'路径下的URL转发给上边的upstream educa,也就是uWSGI的socket进行处理。还把NGINX自带的关于和uwsgi协同工作的参数设置也包含进去。
NGINX还有很多复杂的设置,文档可以参考https://nginx.org/en/docs/。
NGINX主要的设置文件位于/etc/nginx/nginx.conf,该文件包含/etc/nginx/sites-enabled/下的所有配置文件。为了让NGINX使用我们刚才编写的配置文件,打开系统命令行窗口建立一个软连接:
Copysudo ln -s /home/projects/educa/config/nginx.conf /etc/nginx/sites-enabled/educa.conf
将其中的/home/projects/educa/替换成实际的绝对路径。注意,这里如果没有/sites-enabled/目录,要先手工建立。
如果还没有运行uWSGI,打开系统命令行窗口,在educa项目根目录先运行uWSGI:
Copyuwsgi --ini config/uwsgi.ini
当前窗口会被uWSGI占用,再开一个命令行窗口,然后执行:
Copyservice nginx start
由于我们使用了自定义的域名,还必须修改/etc/hosts,添加如下两行:
Copy127.0.0.1 educaproject.com127.0.0.1 www.educaproject.com
这样我们就把这两个域名都路由到本地回环地址上,由于我们是从本机访问本机,所以要更改HOSTS,实际生产环境不必做本步修改,因为生产环境会有固定的IP,域名和对应的DNS解析。
打开浏览器,输入http://educaproject.com/,应该可以看到站点了,但是所有的静态文件依然没有被加载,没关系,即将完成生产环境的配置。
如果系统是Centos 7,这里显示502错误,查看/var/log/nginx/error.log,如果其中的错误是[crit] 4036#4036: *1 connect() to unix:///tmp/educa.sock failed (13: Permission denied),就先执行/usr/sbin/sestatus查看SELINUX的状态,如果为开启,就编辑SELINUX的设置,将其关闭,如下:
Copyvi /etc/selinux/config
#SELINUX=enforcing
SELINUX=disabled
之后reboot重启系统才行。之后应该就可以正常显示站点了。
之后为了安全起见,到settings/pro.py中,修改ALLOWED_HOSTS设置为NGINX配置文件中的两个域名:
CopyALLOWED_HOSTS = ['educaproject.com', 'www.educaproject.com']
现在Django就只为这两个主机名提供服务了。关于ALLOWED_HOSTS的更多信息可以看https://docs.djangoproject.com/en/2.0/ref/settings/#allowed-hosts。
1.10让NGINX提供静态文件和媒体资源服务
NGINX提供静态文件的速度很快。刚才我们把所有的地址转发,都交给了uWSGI,现在要将所有的静态文件通过NGINX提供服务,对于我们站点来说,就是把所有的CSS JS文件和用户上传的媒体文件都交给NGINX来代理。
编辑settings/base.py,增加下边一行:
CopySTATIC_ROOT = os.path.join(BASE_DIR, 'static/')
这行表示存放站点静态文件的地址,还记得之前学习过使用python manage.py collectstatic吗?现在就需要将所有的静态文件收集过来放在此目录中,在命令行中输入:
Copypython manage.py collectstatic --settings=educa.settings.pro
注意,原书的命令缺少了 --settings=educa.settings.pro
可以看到下列输出:
Copy160 static files copied to '/educa/static'.
静态文件目录设置好了,现在需要将这个目录设置到NGINX中,编辑config/nginx.conf,在server指令后的大括号中增加下列内容:
Copylocation /static/ {
alias /home/projects/educa/static/;
}
location /media/ {
alias /home/projects/educa/media/;
}
将其中的/home/projects/educa/static/和/home/projects/educa/media/替换成你项目的实际static和media目录的绝对路径。这两个参数解释如下:
/static/:这个路径是Django中设置的STATIC_URL,表示当NGINX看到/static/的路径请求的时候,就到这个设置对应的路径中寻找所需文件。
/media/:这个路径是Django中设置的MEDIA_URL路径,表示当NGINX看到/media/的路径请求的时候,就到这个设置对应的路径中寻找所需文件。
重新启动NGINX服务,以便让配置文件生效:
Copyservice nginx reload
在浏览器中打开http://educaproject.com/,现在可以看到整个站点包含静态资源都正确的显示了。对于站点的静态文件请求,NGINX将绕开uWSGI,把文件直接返回给浏览器。
现在生产环境就初步配置完毕。整个站点现在可以说运行在生产环境之下了。
1.11使用SSL安全连接
在配置完初步的生产环境之后,下一个话题是站点的安全性。Secure Sockets Layer现在逐渐成为提供Web安全连接服务的规范。强烈建议对于正式的网站使用HTTPS协议,现在就在NGINX中配置SSL认证来让站点变得更加安全。
1.11.1创建一个SSL认证
在educa项目根目录下建立一个ssl目录,然后通过openssl生成我们的SSL证书:
Copysudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ssl/educa.key -out ssl/educa.crt
用这条命令生成一个365天有效的2048位的SSL证书,然后系统会提示输入一些信息:
CopyCountryName (2 letter code) [AU]:
StateorProvinceName (full name) [Some-State]:
LocalityName (eg, city) []:
OrganizationName (eg, company) [Internet Widgits Pty Ltd]:
OrganizationalUnitName (eg, section) []:
CommonName (e.g. server FQDN or YOUR name) []: educaproject.comEmailAddress[]: email@domain.com
这其中最关键的是Common Name,必须将主机域名名称输入:这里使用educaproject.com
之后会在ssl/目录下生成两个文件,educa.key是私钥,educa.crt是实际的SSL证书。
1.11.2配置NGINX使用SSL
编辑config/nginx.conf,在server设置中加入下列内容:
Copyserver {
listen 80;
listen 443 ssl;
ssl_certificate /home/projects/educa/ssl/educa.crt;
ssl_certificate_key /home/projects/educa/ssl/educa.key;
server_name www.educaproject.com educaproject.com;
# ...
}
将其中的路径都修改为SSL证书所在的实际绝对路径。
这么设置之后,NGINX将同时监听80端口(HTTP协议)和443端口(HTTPS协议),然后指定了SSL的验证信息ssl_certificate与对应的密钥ssl_certificate_key。
现在重新启动NGINX服务,访问 https://educaproject.com/ ,会看到类似如下提示:
这个提示因浏览器而异。意思是警告当前站点并没有使用一个值得信任的验证方式,浏览器无法确定该站点安全与否。这是因为我们使用的SSL证书是由我们自行签发的,而不是从一个受信任的机构(Certification Authority)获得的证书。当我们有了实际的公开域名之后,就可以向一个受信任的证书颁发机构申请一个SSL证书,这样浏览器就能识别该站点的HTTPS认证。
如果想为实际的站点申请证书,可以使用Linux基金会Linux Foundation的Let's Encrypt项目。这是一个致力于免费获得和更新SSL证书的计划,该计划的站点在 https://letsencrypt.org/。
点击 "Add Exception" 按钮可以让浏览器知道可以信任该站点,这时浏览器的显示可能如下:
点击小锁按钮,就可以看到SSL的详细信息。
译者注:这里也因浏览器而异,有的浏览器依旧会提示证书不可信或者存在问题,毕竟这个证书是我们自行签发的。
1.11.3配置Django使用SSL
Django也有针对SSL的配置,编辑settings/pro.py,增加下边的代码:
CopySECURE_SSL_REDIRECT = True
CSRF_COOKIE_SECURE = True
这两个设置的含义如下:
SECURE_SSL_REDIRECT:是否所有的HTTP请求都必须被重定向到HTTPS
CSRF_COOKIE_SECURE:是否建立加密cookie防止CSRF攻击
现在我们就配置好了一个高效的提供Web服务的生产环境。
2自定义中间件
在之前我们已经了解了中间件MIDDLEWARE的设置,该设置包含项目中所有使用到的中间件。关于中间件,可以认为其是一个底层的插件系统,为在请求/响应的过程中提供钩子。每一个中间件都负责一个特定的行为,会在HTTP请求和响应的过程中得到执行。
注意不要添加开销非常大的中间件,因为中间件会在项目的所有请求和响应的过程中被执行。
当一个HTTP请求进来的时候,中间件会按照其在MIDDLEWARE设置中从上到下的顺序执行,当HTTP响应被生成且发送的过程中,中间件会按照设置中从下到上的顺序执行。
一个符合标准的函数可以作为一个中间件被注册在settings.py中。类似下边的函数就可以作为一个中间件:
Copydefmy_middleware(get_response):
defmiddleware(request):
# 对于每个HTTP请求,在视图和之后的中间件执行之前执行的代码
response = get_response(request)
# 对于每个HTTP请求和响应,在视图执行之后执行的代码return response
return middleware
一个中间件工厂函数接受一个get_response可调用对象,然后返回一个中间件函数。一个中间件接受一个请求然后返回一个响应,类似于视图。这里的get_response可以是下一个中间件,如果自己就是中间件列表中的最后一个,也可以是一个视图名称。
如果任何一个中间件在尚未调用get_response这个可调用对象之前就返回了一个响应,这个时候就会短路整个中间件链条的处理:其后的中间件不再被执行,这个响应开始从同级的中间件向上返回。
所以MIDDLEWARE设置中的中间件顺序非常重要,因为中间件依赖于上下中间件的数据进行工作。
在向MIDDLEWARE中添加一个中间件时必须注意将其放置在正确的位置,反复强调,中间件在HTTP请求进来的时候从上到下执行,HTTP响应发出的时候从下到上执行。
原书在这里只是比较简单的说了一下执行顺序,详细的中间件执行顺序请参考Django进阶-中间件以及https://docs.djangoproject.com/en/2.0/topics/http/middleware/。
2.1创建二级域名中间件
我们来创建一个自定义中间件,用于通过一个自定义的二级域名来访问课程资源。例如:某个显示课程的URL:https://educaproject.com/course/django/,可以通过一个二级域名django.educaproject.com来访问。这样用户就可以使用二级域名作为快捷方式快速访问课程,也比较容易记忆该路径。所有发往这个二级域名的请求,都会被重定向到实际的educaproject.com/course/django/这个URL。
与视图,模型,表单等组件一样,中间件也可以写在项目的任何位置。推荐在应用目录内建立middleware.py文件来编写中间件。
在courses应用目录内创建middleware.py文件,并编写如下代码:
Copyfrom django.urls import reverse
from django.shortcuts import get_object_or_404, redirect
from .models import Course
defsubdomain_course_middleware(get_response):
"""
为课程提供二级域名
"""defmiddleware(request):
host_parts = request.get_host().split('.')
iflen(host_parts) > 2and host_parts[0] != 'www':
# 通过指定的二级域名查询课程对象
course = get_object_or_404(Course, slug=host_parts[0])
course_url = reverse('course_detail', args=[course.slug])
# 将二级域名请求重定向至实际的URL
url = '{}://{}{}'.format(request.scheme, '.'.join(host_parts[1:]), course_url)
return redirect(url)
response = get_response(request)
return response
return middleware
当一个HTTP请求进来的时候,这个中间件执行如下任务:
取得这个HTTP请求中的域名,然后将其分割成几部分;例如mycourse.educaproject.com会被分割得到一个列表['mycourse', 'educaproject', 'com']
检查这个域名是否包含二级域名,判断分割后的域名是否包含多于2个元素。如果包含,就取出第一个元素也就是二级域名,如果这个域名不是www,那就通过根据slug查询并取得该课程对象。
如果找不到对应的课程,就返回404错误;如果找到了,就重定向到课程对象对应的规范化URL。
编辑settings/base.py,把自定义中间件添加到MIDDLEWARE设置中:
CopyMIDDLEWARE = [
# ......'courses.middleware.subdomain_course_middleware',
]
还需要看一下ALLOWED_HOSTS中的域名设置,这里我们将其设置为可以是任何eduproject.com的二级域名:
CopyALLOWED_HOSTS = ['.educaproject.com']
ALLOWED_HOSTS中以一个.开始的域名,例如.educaproject.com,会匹配educaproject.com及所有的educaproject.com的二级域名,比如course.educaproject.com和django.educaproject.com。
2.3配置NGINX的二级域名
编辑config/nginx.conf,将以下这行:
Copyserver_name www.educaproject.com educaproject.com;
修改成:
Copyserver_name *.educaproject.com educaproject.com;
通过增加通配符设置,让NGINX也可以代理所有的二级域名,为了测试中间件,还必须在etc/hosts中配置相关内容,比如如果要测试二级域名django.educaproject.com,需要增加一行:
Copy127.0.0.1 django.educaproject.com
然后启动站点到https://django.educaproject.com/,可以发现中间件现在将其重定向到 https://educaproject.com/course/django/
3实现自定义的管理命令
Django允许应用向manage.py管理工具中注册自定义的管理命令。所谓管理命令就是通过manage.py使用的指令,例如,我们曾经使用在第9章使用过makemessages和compilemessages命令。
一个管理命令由一个Python模块组成,这个模块里包含一个Command类,这个Command类继承django.core.management.base.BaseCommand或者BaseCommand的子类。我们可以创建一个简单的包含参数和选项的自定义命令。
对于每个在INSTALLED_APPS内注册的应用,Django会在应用目录下边的management/commands/目录下搜索管理命令,搜索到的每个命令模块,都会被注册成为一个同名的命令。
更多自定义管理命令的信息可以查看https://docs.djangoproject.com/en/2.0/howto/custom-management-commands/。
我们准备来创建一个提醒学生至少选一个课程的命令。这个命令会向所有已经注册超过一定时间,但还没有选任何一门课程的学生发送一封邮件。
在student应用下建立如下的目录和文件结构:
Copymanagement/
__init__.py
commands/
__init__.py
enroll_reminder.py
编辑enroll_reminder.py,添加下列代码:
Copyimport datetime
from django.conf import settings
from django.core.management.base import BaseCommand
from django.core.mail import send_mass_mail
from django.contrib.auth.models import User
from django.db.models import Count
classCommand(BaseCommand):
help = 'Sends an e-mail reminder to users registered more than N days that are not enrolled into any courses yet'defadd_arguments(self, parser):
parser.add_argument('--days', dest='days', type=int)
defhandle(self, *args, options):
emails = []
subject = 'Enroll in a course'
date_joined = datetime.date.today() - datetime.timedelta(days=options['days'])
users = User.objects.annotate(course_count=Count('courses_joined')).filter(course_count=0,
date_joined__lte=date_joined)
for user in users:
message = "Dear {},\n\n We noticed that you didn't enroll in any courses yet. What are you waiting for?".format(
user.first_name)
emails.append((subject, message, settings.DEFAULT_FROM_EMAIL, [user.email]))
send_mass_mail(emails)
self.stdout.write('Sent {} reminders'.format(len(emails)))
这是enroll_reminder命令,解释如下:
Command类继承BaseCommand类
Command类包含一个help属性,为命令提供帮助信息,运行python manage.py help enroll_reminder就可以看到这段信息。
add_arguments()用来设置可用的参数,这里设置了--days参数,指定其类型为整型。运行命令时这个参数用于指定天数,方便筛选出要向其发送邮件的学生。
handle()方法定义命令的实际业务逻辑。这里从命令行中获取解析后的days属性,然后查询注册时间超过该天数的用户,再通过分组计算这些用户的选课数量,从中选出未选课的用户。然后使用一个emails列表记录所有需要发送的邮件,最后通过send_mass_mail()方法发送邮件,这样可以使用一个SMTP链接发送大量邮件,而不用每发一次邮件就新开一个SMTP链接。
编写好上述代码后,打开系统命令行来运行命令:
Copypython manage.py enroll_reminder --days=20
如果还没有配置SMTP服务器,可以参考第二章中的内容。如果确实没有SMTP服务器,可以在settings.py中加上:
CopyEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
以让Django将邮件内容显示在控制台而不实际发送邮件。
还可以通过系统让这个命令每天早上8点运行,如果使用了基于UNIX的操作系统,可以打开系统命令行模式,输入crontab -e来编辑crontab,在其中增加下边这行:
Copy0 8 * * * python /path/to/educa/manage.py enroll_reminder --days=20 --settings=educa.settings.pro
将其中的/path/to/educa/manage.py替换成实际的manage.py所在的绝对路径。如果不熟悉cron的使用,可以参考http://www.unixgeeks.org/security/newbie/unix/cron-1.html。
如果使用的是Windows,可以使用系统的计划任务功能,具体可以参考https://docs.microsoft.com/zh-cn/windows/desktop/TaskSchd/task-scheduler-start-page。
还有一个方法是使用Celery定期执行任务。我们在第7章使用过Celery,可以使用Celery beat scheduler来建立定期执行的异步任务,具体可以参考https://celery.readthedocs.io/en/latest/userguide/periodic-tasks.html。
对于想通过cron或者Windows的计划任务执行的单独脚本,都可以通过自定义管理命令的方式来进行。
Django还提供了一个使用Python执行管理命令的方法,可以通过Python代码来运行管理命令,例如:
Copyfrom django.core import management
management.call_command('enroll_reminder', days=20)
程序在执行到这里的时候,就会去运行这个命令。现在我们就可以为自己的应用定制管理命令并且计划运行了。
总结
这一章里使用uWSGI和NGINX配置完成了生产环境,还实现了自定义中间件和管理命令。
到这里本书已经结束。祝贺你,本书通过创建实际的项目和将其他软件与Django集成的方式,指引你学习使用Django建立Web应用所需的技能。无论一个简单的项目原型还是大型的Web应用,你现在都具备使用Django创建它们的能力。
学习方向
学习可不是拼多多,这里学一点哪里学一点、到头来竹篮打水一场空,所以得有个自动化测试所有方向技术点做一份整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
一、Python编程从入门到精通专题
Python编程从入门到精通专题
二、接口自动化测试项目实战专题
接口1图
接口自动化入门
三、WEB自动化测试项目实战专题
四、App自动化测试项目实战专题
根据这个学习架构路线,不断地去摸索与提升,突破技术的瓶颈,可以说,这个过程会让你痛不欲生,但只要你熬过去了。以后的生活就轻松很多。我也是走过这样一段路,才能获得更多高薪职位的机会,付出终有回报,也算是对我能力的一种认可吧,真正的证明了自己的价值。至少税后30+的薪水是我当前状态下比较满意的。
作为一位过来人也是希望你们少走一些弯路,在这里我给大家分享一些自动化测试的相关资料,希望帮你们更好的突破自己,提高自己!需要的可以来一手三连支持笔者,我把这些资料的都放在这个群953306497了,需要的自行领取哦。
精品软件测试学习书籍
1.技术沉淀(学习+积累)
对于三年多快四年的测试人员来说,无论是在大厂小厂,还是在外包公司,基本上都是做得功能界面测试,对接口、性能、自动化测试只是有点接触。很多人认为,重复简单的工作对自己的技术是没有提升的,但实际上也有人能从中杀出一条血路,所以关键是你自己是否花了心思,付出了努力。
当然,我个人也有自己的一套学习方式和习惯,平时喜欢利用碎片时间来看点什么东西,比如一些技术性的书籍或文档,还有一些视频讲解等,经常会让我有一种恍然大悟的感觉,对我的工作也起到了很大的帮助。
阅读书籍文档
之前有些做手工测试的同事想转自动化测试,让我推荐几本自动化测试入门的书籍, 我都会建议他们根据自己所在项目的产品来选用对应的自动化测试工具来实践,再结合自身的喜好来选书, 下面推荐几本实用性强的书籍。
1)《Selenium 3自动化测试实战:基于Python语言》
selenium 3自动化测试实战这本书是大佬虫师的作品,虫师可以说是我的启蒙人了,早期看了他很多的文章。这本书系统的讲解最新的selenium 3的应用,涉及到持续集成、两大python单元测试框架unittest和pytest的应用,还有数据驱动、PO模型、selenium的分布式运行等等,非常全面,值得入手。
2)《QTP自动化测试最佳实践》
详细介绍了基于QTP的自动化测试技术,适合自动化测试初学者阅读。因为QTP是付费软件,如果所在公司或项目组能使用正版,那真是个深入学习的好机会。
3)《自动化测试最佳实践》
这本书在自动化测试领域影响深远,它为很多公司指明了测试成功的方向。本书讲述了一些公司应用自动化的经历,其中涉及了很多项目,从复杂的政府部门系统到医疗设备,从SAP业务过程开发到Android移动应用和云计算。
4)《App自动化测试与框架实战》
从App测试基础、实战技术,再到自动化测试框架的搭建,全面地讲解App测试所需要的知识,主要内容为:App测试及其类型、Java编程环境构建、Java语言基础、Android自动化环境精讲、Android自动化测试基础精讲、Android Appium自动化框架、Appium数据驱动测试框架封装实战、Appium关键字驱动测试框架封装实战、持续集成的自动化、Appium常见问题处理方式。
5)《postman接口自动化测试持续集成》
如果你正好需要利用postman完成接口自动化测试的工作,那么这本书很适合你。如果你想看看 postman 是如何完成自动化测试,并与自己的框架比对比对,这本书也适合阅读。这本书,详细讲解了接口测试基础、postman 如何来完成自动化设计和用例编写、newman+git+jenkins+ 钉钉来完成持续集成工作。
实战案例
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
电商项目实战
web测试项目
web+App+h5+小程序 测试项目
接口自动化测试实战项目
Linux实战项目
面试资料
我们进阶学习自动化测试必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
以上资料,对于想要测试进阶的朋友们来说应该会很有帮助,需要的小伙伴可以后台私信找我免费领取。
总结
我见过很多leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了好几年,更夸张的是7、8年工作内容的重复性比较高,没有什么技术含量的工作。
凡事要趁早,特别是技术行业,一定要提升技术功底,丰富自动化项目实战经验,这对于你未来几年职业规划,以及测试技术掌握的深度非常有帮助。
如果对你有帮助的话,点个赞收个藏,给作者一个鼓励。也方便你下次能够快速查找。
如有不懂还要咨询下方小卡片,博主也希望和志同道合的测试人员一起学习进步
在适当的年龄,选择适当的岗位,尽量去发挥好自己的优势。
我的自动化测试开发之路,一路走来都离不每个阶段的计划,因为自己喜欢规划和总结,
测试开发视频教程、学习笔记领取传送门!!!