Django - 生产环境要注意哪些事项

生产环境部署前的注意事项

  • 单元测试:版本质量评估
  • 生产环境 Django 配置

单元测试 - 测试用例基类层次

  • SimplTestCase:可以发起 HTTP 请求,跟页面,模板,URL 交互,禁止了数据库的访问
  • TransactionTestCase:在用例运行之后,清理所有表来重置数据库;可以运行提交、回滚来观察中间状态(需要测试事务时使用)
  • TestCase:测试用例执行完后不清理表数据;在一个事务中执行用例,最后自动回滚事务
  • LiveServerTestCase:在后台自动启动一个 Server,以便使用外部工具如 Selenium 做测试

单元测试

一个简单的测试用例

继承自 TestCase,定义了 classmethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.test import TestCase


class MyTestCase(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
...

@classmethod
def tearDownClass(cls):
...
super().tearDownClass()

def setUp(self):
# Setup run before every test method.
pass

def tearDown(self):
# Clean up run after every test method.
pass

def test_something_that_will_pass(self):
self.assertFalse(False)

单元测试 - 目录结构组织

  • 哪些逻辑需要测试?

    • Django 自带的代码(框架中实现的)逻辑不需要测试
    • 自己写的代码需要测试,比如自定义的页面的访问,自定义的功能菜单
  • 测试用例目录组织

    Django 使用 unittest 模块的内置测试查找机制, 它将在当前工作目录下,查找任何匹配模式 test*.py 命名的文件作为 Test Case。

    测试用例目录组织

执行测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 运行项目下面所有 test
$python manage.py test

# 测试指定模块
$python manage.py test jobs.testcase

# 测试单个模块中的文件
$python manage.py test jobs.testcase.test_views

# 指定类
$python manage.py test jobs.testcase.test_views.JobTests

# 测试指定方法
$python manage.py test jobs.testcase.test_views.JobTests.test_detail

示例

在应用目录下创建一个名为 testcase 的 python 包,然后在该包下创建一个名为 test_views.py 的文件,里面的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from django.test import TestCase
from django.test import Client

from jobs.models import Job, JobTypes, Cities


class JobTests(TestCase):

@classmethod
def setUpTestData(cls):
# Set up data for the whole TestCase
cls.job = Job.objects.create(job_name="Java开发工程师", job_type=JobTypes[0][0], job_city=Cities[1][0], job_requirement="精通Java开发")

def test1(self):
# Some test using self.job
pass

def test_index(self):
client = Client()
response = client.get('/joblist/')
self.assertEqual(response.status_code, 200)

def test_detail(self):
# 使用 TestCase.self.client 作为 HTTP Client:
response = self.client.get('/job/1/')
self.assertEqual(response.status_code, 200)

job = response.context['job']
self.assertEqual(job.job_name, JobTests.job.job_name)

启动测试:python manage.py test

发布到生产环境的步骤

  1. 配置生产环境配置 (settings):DEBUG&Secret 相关信息
  2. 选择 Django App 的托管环境 (IaaS/PaaS,比如阿里云 / AWS/Azure/GAE/Heroku 等等)
  3. 部署前的安全检查
  4. 选择静态资源文件的托管环境(包插 JS/CSS/ 图片 / 文件等)& 部署静态资源
  5. 部署 Django 应用容器 & Web 服务器

发布到生产环境的步骤

配置生产环境配置 - 让网站准备好发布

必须调整的关键配置是:

  • DEBUG:在生产环境中设置为 False(DEBUG = False);避免在 web 页面上显示敏感的调试跟踪和变量信息
  • SECRET_KEY:这是用于 CSRF 保护的随机值
  • ALLOWED_HOSTS:生产环境必须设置允许访问的域名
  • 生成 SECRET KEY
    1
    python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'

配置生产环境配置 - 密钥的存储和管理

  • 从环境变量读取配置, 或从配置文件中读取

    1
    2
    3
    4
    5
    import os

    DEBUG = False
    SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'xxxxzz')
    ALLOWED_HOSTS = ["127.0.0.1", "recruit.ihopeit.com", ]
  • 从 KMS 系统中读取配置的密钥

    • 自己部署的 KMS 系统
    • 云服务的 KMS 服务:阿里云 / AWS 的 KMS 服务

部署到生产环境

部署前的安全检查

1
python manage.py check --deploy

部署前的安全检查

静态资源文件的托管环境

  • 静态内容 Web 服务器: Apache/Nginx
  • CDN 服务器

collectstatic 工具:用来收集静态资源文件,settings 中的相关设置:

  • STATIC_URL:能够访问到静态文件的 URL 路径
  • STATIC_ROOT:collectstatic 工具用来保存收集到的项目引用到的任何静态文件的路径
  • STATICFILES_DIRS:这列出了 Django 的 collectstatic 工具应该搜索静态文件的其他目
1
2
3
# 在settings.py文件中添加如下配置
STATIC_URL = '/static/'
STATIC_ROOT = 'static'
1
$python manage.py collectstatic --settings=settings.local

收集完成后,可以将这些静态文件,上传到托管文件的服务器 / CDN

Django 应用容器

  • 同步应用

    • uWSGI: C 实现的 Python Web 容器;Web 服务器 Apache/Nginx 与 django-uwsgi 进程通信来提供动态的内容
    • gunicorn:纯 Python 实现的高性能 Python 应用容器,无外部依赖,简单容易配置; 还没有遇到性能问题的时候,推荐使用 gunicorn
  • 异步应用

    • Daphne:twisted 实现
    • Hypercorn:基于 sans-io hyper,h11,h2,wsproto 实现
    • Uvicorn:基于 uvloop and httptools 实现(推荐)

Django 应用容器 - 异步支持 Roadmap

  • Django 的异步支持 Roadmap

    • Django 3.0 - ASGI Server
    • Django 3.1 - Async Views
    • Django 3.2/4.0 - Async ORM
  • 异步视图

    1
    2
    3
    async def view(request):
    await asyncio.sleep(0.5)
    return HttpResponse("Hello,async world ! ")

启动服务器

  • 同步应用服务器,以 gunicorn 为例

    1
    2
    3
    $ python -m pip install gunicorn
    $ export DJANGO_SETTINGS_MODULE=settings.local
    $ gunicorn -w 3 -b 127.0.0.1:8000 recruitment.wsgi:application

    -w 3:表示启动 3 个 worker 进程
    -b ip:port:表示绑定的 ip 地址和端口

  • 异步应用服务器,以 uvicorn 为例

    1
    2
    3
    $ python -m pip install uvicorn
    $ export DJANGO_SETTINGS_MODULE=settings.local
    $ uvicorn recruitment.asgi:application --workers 3 --host 127.0.0.1 --port 8000

    --work:表示启动 3 个 worker 进程
    --host:表示主机的 IP 地址
    --port:表示启动使用的端口号