Django 后端学习路线
推荐从上往下看。
- 官方快速安装指南(3.1 版本),安装的教程
- 官方中文教程(3.1 版本),开发的教程
- MDN 上的 Django 教程,和上面的教程有重叠,推荐只看会话和用户认证部分
- Django REST Framework,这是一个基于 Django 的 RESTful 后端框架,为常见 RESTful 操作提供了模板,大大降低 REST API 开发量
Django 官方教程 关键步骤
本小节记录了 官方中文教程(3.1 版本) 中的关键步骤。
1. 创建项目、项目和一个视图
- 安装:
pip install Django
- 验证安装:
python -m django --version
- 创建并初始化项目文件夹:
django-admin startproject <projectname>
- 即时预览:在
<projectname>
目录下python manage.py runserver [port]
- 创建应用:
python manage.py startapp <appname>
- 编写视图:
1 | # polls/views.py |
- 在应用中添加写好的视图:
1 | # polls/urls.py |
- 在站点中添加应用的视图:
1 | # mysite/urls.py |
2. 数据库使用、管理员
2.1 配置数据库
- 安装 mysqlclient:
pip install mysqlclient
- 修改项目配置文件:
1 | # mysite/settings.py |
2.2 创建模型并迁移至数据库
一个 Django 模型对于一个 SQL 数据表。
- 创建模型:
1 | # polls/models.py |
- 激活模型:
1 | # mysite/settings.py |
- 将模型更改写入数据库:
- 根据类的更改,生成一个
迁移
(一个存储在<app_lable>/migrations
下的 py 文件,存储了变化):python manage.py makemigrations [app_label]
(文档) - 将一个
迁移
应用到数据库,并迁移数据:python manage.py migrate [app_label] [migration_name]
(文档) - 查看一个
迁移
将对数据库造成的影响:python manage.py sqlmigrate <app_label> <migration_name>
(文档) - 一般来说,类变更以后,需要:
python manage.py makemigrations && python manage.py migrate
- 第一次部署的时候,需要
python manage.py makemigrations <app1> <app2> <...appn> && python manage.py migrate
- 根据类的更改,生成一个
对了,migrations
文件夹应当加入 .gitignore
,否则不同开发者的 migrations
就要冲突啦。
2.3 数据库 API
- 进入 Python 命令行:
python manage.py shell
- 使用前先引入类:
from polls.models import Choice, Question
对于一个数据表:
- 一个表的所有元素:
Question.objects.all()
- 以成员筛选记录:
Question.objects.filter(id=1)
- 以
pub_date
成员的year
成员筛选(成员方法同理):pub_date.year
:Question.objects.filter(pub_date__year)
对于一个记录:
- 构造一个新记录:
q = Question(question_text="What's new?", pub_date=timezone.now())
- 将记录插入表:
q.save()
- 查询、修改记录的属性(同理可调用其方法):
q.question_text
- 删除一个记录:
q.delete()
对于一个外键(Choice
存在外键,为 Question
):
- 查询一个 Choice 对应的 Question:
c.qeustion
- 查询一个 Question 对应的 Choice:
q.choice_set.all()
- 为 Question 创建一个 Choice:
q.choice_set.create(choice_text='Not much', votes=0)
2.4 管理员相关
- 创建管理员:
python manage.py createsuperuser
- 管理员登录界面:
http://127.0.0.1:8000/admin/
- 在管理员页面中添加
Question
模型:
1 | # polls/admin.py |
3. 视图和 urls
3.1 添加更多视图,并用参数匹配 url
1 | # /polls/views.py |
1 | # /polls/url.py |
访问 /polls/34
会返回 You're looking at question 34.
3.2 使用 HTML 模板
编写一个 HTML 模板:
1 | <!-- /polls/templates/polls/index.html --> |
再在视图中:加载模板、用数据渲染、然后转为 HTTP Response,三步使用 django.shortcuts.render()
完成
1 | from django.shortcuts import render |
3.3 抛出 404 错误码
1 | from django.http import Http404 |
也可以使用 django.shortcuts.get_object_or_404
。该函数在 object
不存在会 raise Http404()
:
1 | from django.shortcuts import get_object_or_404, render |
也有 get_list_or_404()
函数,工作原理和 get_object_or_404()
一样,除了 get()
函数被换成了 filter()
函数。如果列表为空的话会抛出 Http404
异常。
3.4 使用 name 替代 URL 中的硬编码、为 URL 名称添加命名空间(app_name)
https://docs.djangoproject.com/zh-hans/3.1/intro/tutorial03/#removing-hardcoded-urls-in-templates
https://docs.djangoproject.com/zh-hans/3.1/intro/tutorial03/#namespacing-url-names
4. 编写一个简单的表单
因为我想用 Django 做纯 REST 后端,所以这部分略。
https://docs.djangoproject.com/zh-hans/3.1/intro/tutorial04/
5. 测试
https://docs.djangoproject.com/zh-hans/3.1/intro/tutorial05/
关于测试还是值得单独拿一个章节出来的:测试
6. 插入静态文件
https://docs.djangoproject.com/zh-hans/3.1/intro/tutorial06/
7. 修改 Admin 页面
https://docs.djangoproject.com/zh-hans/3.1/intro/tutorial07/
如果想要修改某元素对应外键的信息(而不是修改其外键),可以参考 django.contrib.admin.StackedInline
如果想要汉化 Admin 页面,可以参考:https://blog.csdn.net/aaazz47/article/details/78666099
Django 用户认证
Django 用户认证(后端篇)
MDN 教程:https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Django/Authentication
文档:https://docs.djangoproject.com/zh-hans/3.1/topics/auth/default/
- 创建用户:
user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
- 创建超级用户:在命令行中
python manage.py createsuperuser
- 登录:
1 | from django.contrib.auth import authenticate, login |
- 判断用户身份:可以通过
request.user.is_authenticated==False
表示为匿名者;否则request.user
会被设置为User
实例。 - 更改密码:
1 | from django.contrib.auth.models import User |
login_required
装饰器:
1 | from django.contrib.auth.decorators import login_required |
- 登出:
django.contrib.auth.logout(request)
Django 用户认证(前端篇)
Django 的用户认证是用 Session 实现的,和其他的 Session 应该是类似的。但对于零基础前后端开发的我,不清楚这之中究竟发生了什么。于是我简单测试了一下。
在登录成功后,应答的 headers 中就会出现 Set-Cookies
字段:
1 | $ http POST http://127.0.0.1:8000/api/accounts/login/ <<< '{"username":"lyh543@outlook.com", "password":"xxxxxxxx"}' |
前一个 csrftoken
是防止跨站请求的,如果项目是前后端分离的话,就需要进行配置(关于 CSRF,可以看 和 CSRF 与 CORS 斗智斗勇);
后一个 sessionid
就是登录成功后的 sessionid
了。如果我们在下次请求中的 headers 中加入了这个 sessionid
,服务器就能识别到我们。对于 Django 来说,就是 request.user
为登录的这个用户。
对于浏览器、requests.sessions.Session
等,会自动设置 Cookie。下面是利用 requests.sessions.Session
完成登录、查询管理员字段的过程:
1 | In [2]: import requests |
而对于非登录操作、或登录失败,应答中的字段就不会有 Set-Cookies
字段,requests.sessions.Session
也不会设置 Cookies
:
1 | In [2]: import requests |
Django Session 的过期时间也是可以通过修改 SESSION_COOKIE_AGE
来修改的。
使用 JWT 进行身份验证
Django 自带的 Session 对于很多项目已经够用了。如果想要更高级一点的安全验证,如 Json Web Token,可以尝试 Simple JWT 配合 Django REST Framework 食用。文档给的示例代码很详细,有需要也可以仿照源码编写自己的 API。
Django 定时任务
可参考 Django-crontab。
Django REST Framework
这部分就另开一篇博文来写了。
Django 项目部署 (WSGI)
Django 部署可以采用 WSGI,也可以使用 ASGI。WSGI 是为同步 Web Server 编写的,而 ASGI 是为异步 Web Server 编写的。虽然可以混用,但是同步函数和异步函数可以混用,但是会有约 1ms 的用于线程切换的性能损失。
。如果你主要使用的是异步函数,你可以快进到下一章,进行 ASGI 的部署。看完以后,再回来看看如何 处理静态文件。
Gunicorn
诚然,python manage.py runserver 8000
然后将 8000 端口交给 Nginx / Apache / Caddy 反向代理到 80(http) / 443(https),是最简单且最直接的方法。但是,其替代方案有多线程、占用内存小等优势。
Django 的管理命令
startproject
生成了一个最小化的默认 WSGI 配置,你可以按照自己项目的需要去调整这个配置,任何兼容 WSGI 的应用程序服务器都可以直接使用。
而其中一个 WSGI 应用程序服务器的方案,就是使用 Gunicorn。由于细节比较多,各位先不要急着实践,建议先通读这部分,再决定是否采用这种方式还是直接 startproject
。
安装 Gunicorn:
1 | python -m pip install gunicorn |
在项目文件夹下运行:
1 | gunicorn -b "127.0.0.1:8000" <projectname>.wsgi |
其中 <projectname>.wsgi
也是 Python 的模块的表示方法,其表示 ./<projectname>/wsgi.py
这个模块。
可以将执行这条命令的过程写为 Systemd 服务,并实现 2 进程、每进程 3 线程,以及自动重启等:
1 | # djangoproject.service |
命令的 --access-logfile -
表示将 log 输出在控制台,在 Systemd 中即表示可以通过 systemctl status djangoproject
查询日志。
然后就是将这项服务复制到 /etc/systemd/system/
,然后 enable 和 start 了:
1 | sudo cp ./djangoproject.service /etc/systemd/system/ |
处理静态文件
但是!这并没有完成部署。访问 localhost:8000
时,可以看到 Django 有正常响应,但是所有静态文件全部失效,Swagger 文档生成也失效了。
为了解决这个问题,需要配置静态文件。
在 <projectname>/settings.py
中配置以下几个参数
1 | import os |
三个参数的意义如下:
BASE_DIR/static
是开发中静态文件所在文件夹BASE_DIR/.static
是项目生成后静态文件所在文件夹,应当加入.gitignore
/api/static/
是在网页中访问静态文件的路径
整个过程是这样的:
- 开发者将所需的静态文件放入
BASE_DIR/static
- 开发者运行
python3 manage.py collectstatic
,Django 将开发者提供的BASE_DIR/static
文件,和 Swagger 等 APP 提供的静态文件,一并复制进BASE_DIR/.static
- 用户在浏览器中访问
/api/static/
路径,表示用户想访问的文件夹是BASE_DIR/.static
所以还需要进行以下两步:
- 运行
python3 manage.py collectstatic
- 通过 Nginx / Apache / Caddy 等将静态文件提供给用户
Gunicorn 提供了一个 Nginx.conf 配置模板,我也提供一份 Caddy 的配置模板:
1 | { |
需要注意的是,这种配置的前提是所有 REST API 放在了 /api/
下,这种方法使用的 <projectname>/urls.py
如下:
1 | api_urlpatterns = [ |
Gunicorn 日志 ip 总是显示 127.0.0.1
出现这个问题,我第一反应是 Caddy 反代的锅,第二反应是 Django 的锅,最后查了一下才发现是 Gunicorn 的锅。
Gunicorn doesn’t log real ip from nginx - Stack Overflow
回答也说的很清楚,只需要按照格式修改好后追加到 --access-logformat
参数即可。
我把时间、两个 -
和 127.0.0.1 去掉以后的配置如下:
1 | ExecStart=/usr/local/bin/gunicorn -b "127.0.0.1:8000" \ |
Django 项目部署 (ASGI)
Django 主推的 ASGI 部署方式,应该是它自己维护的 Daphne。
安装 Daphne
1 | python -m pip install daphne |
在项目文件夹下运行:
1 | daphne <projectname>.asgi:application |
1 | ExecStart=/usr/local/bin/daphne -p 8000 <projectname>.asgi:application |
目前 Daphne 还不支持多进程,如需多进程,请使用 uvicorn
。
项目开源地址
上面提到的项目开源在 GitHub。