更新了Django部分的文档

pull/562/merge
jackfrued 2020-07-15 12:12:49 +08:00
parent 5d552344b6
commit cdb7fddd3a
7 changed files with 445 additions and 38 deletions

View File

@ -334,12 +334,12 @@ Teacher.objects.filter(subject__name__contains='全栈')
```Python
from django.contrib import admin
from polls.models import Subject, Teacher
from polls.models import Subject, Teacher
class SubjectModelAdmin(admin.ModelAdmin):
list_display = ('no', 'name', 'intro', 'is_hot')
list_display = ('no', 'name', 'intro', 'is_hot')
search_fields = ('name', )
ordering = ('no', )
@ -388,7 +388,7 @@ from polls.models import Subject, Teacher
})
except (ValueError, Subject.DoesNotExist):
return redirect('/')
```
```
2. 修改`templates/subjects.html`和`templates/teachers.html`模板页。
@ -566,7 +566,7 @@ from polls.models import Subject, Teacher
13. 定义`__str__`方法。
14. 不要将数据文件放在同一个目录中。
> 说明以上内容来自于STEELKIWI网站的[*Best Practice working with Django models in Python*](https://steelkiwi.com/blog/best-practices-working-django-models-python/),有兴趣的小伙伴可以阅读原文。
> **说明**以上内容来自于STEELKIWI网站的[*Best Practice working with Django models in Python*](https://steelkiwi.com/blog/best-practices-working-django-models-python/),有兴趣的小伙伴可以阅读原文。
#### 模型定义参考

View File

@ -1,6 +1,6 @@
## 前后端分离开发入门
在传统的Web应用开发中大多数的程序员会将浏览器作为前后端的分界线。将浏览器中为用户进行页面展示的部分称之为前端而将运行在服务器为前端提供业务逻辑和数据准备的所有代码统称为后端。所谓前后端分离的开发就是前后端工程师约定好数据交互接口并行的进行开发和测试后端只提供数据不负责将数据渲染到页面上前端通过HTTP请求获取数据并负责将数据渲染到页面上这个工作是交给浏览器中的JavaScript代码来完成。
在传统的Web应用开发中大多数的程序员会将浏览器作为前后端的分界线。将浏览器中为用户进行页面展示的部分称之为前端而将运行在服务器为前端提供业务逻辑和数据准备的所有代码统称为后端。所谓前后端分离的开发就是前后端工程师约定好数据交互接口并行的进行开发和测试后端只提供数据不负责将数据渲染到页面上前端通过HTTP请求获取数据并负责将数据渲染到页面上这个工作是交给浏览器中的JavaScript代码来完成。
使用前后端分离开发有诸多的好处,下面我们简要的说下这些好处:
@ -30,9 +30,9 @@ def show_subjects(request):
上面的代码中,我们通过循环遍历查询学科得到的`QuerySet`对象,将每个学科的数据处理成一个字典,在将字典保存在名为`subjects`的列表容器中,最后利用`JsonResponse`完成对列表的序列化向浏览器返回JSON格式的数据。由于`JsonResponse`序列化的是一个列表而不是字典,所以需要指定`safe`参数的值为`False`才能完成对`subjects`的序列化,否则会产生`TypeError`异常。
可能大家已经发现了自己写代码将一个对象转成字典是比较麻烦的如果对象的属性很多而且某些属性又关联到一个比较复杂的对象时情况会变得更加糟糕。为此我们可以使用一个名为bpmappers的三方库来简化将对象转成字典的操作这个三方库本身也提供了对Django框架的支持。
可能大家已经发现了,自己写代码将一个对象转成字典是比较麻烦的,如果对象的属性很多而且某些属性又关联到一个比较复杂的对象时,情况会变得更加糟糕。为此我们可以使用一个名为`bpmappers`的三方库来简化将对象转成字典的操作这个三方库本身也提供了对Django框架的支持。
安装三方库bpmappers。
安装三方库`bpmappers`
```Shell
pip install bpmappers
@ -63,15 +63,24 @@ def show_subjects(request):
return JsonResponse(subjects, safe=False)
```
配置URL映射然后访问该接口可以得到如下所示的JSON格式数据。
配置URL映射。
```Python
urlpatterns = [
path('api/subjects/', show_subjects),
]
```
然后访问该接口可以得到如下所示的JSON格式数据。
```JSON
[
{
"no": 101,
"no": 1,
"name": "Python全栈+人工智能",
"intro": "Python是一种计算机程序设计语言。是一种面向对象的动态类型语言最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越来越多被用于独立的、大型项目的开发。",
"create_date": "2017-08-01",
"is_hot": true
},
// 此处省略下面的内容
@ -92,7 +101,7 @@ class SubjectMapper(ModelMapper):
class Meta:
model = Subject
exclude = ('create_date', 'is_hot')
exclude = ('is_hot', )
```
再次查看学科接口返回的JSON数据。
@ -109,11 +118,11 @@ class SubjectMapper(ModelMapper):
]
```
关于bpmappers详细的使用指南请参考它的[官方文档](<https://bpmappers.readthedocs.io/en/stable/>),这个官方文档是用日语书写的,可以使用浏览器的翻译功能将它翻译成你熟悉的语言即可。
关于`bpmappers`详细的使用指南,请参考它的[官方文档](<https://bpmappers.readthedocs.io/en/stable/>),这个官方文档是用日语书写的,可以使用浏览器的翻译功能将它翻译成你熟悉的语言即可。
### 使用Vue.js渲染页面
关于Vue.js的知识我们在第21天到第30天的内容中已经介绍过了这里我们不再进行赘述。如果希望全面的了解和学习Vue.js建议阅读它的[官方教程](<https://cn.vuejs.org/v2/guide/>)或者在[YouTube](<https://www.youtube.com/>)上搜索Vue.js的新手教程Crash Course进行学习。
接下来我们通过前端框架Vue.js来实现页面的渲染。如果希望全面的了解和学习Vue.js建议阅读它的[官方教程](<https://cn.vuejs.org/v2/guide/>)或者在[YouTube](<https://www.youtube.com/>)上搜索Vue.js的新手教程Vue.js Crash Course进行学习。
重新改写subjects.html页面使用Vue.js来渲染页面。
@ -122,36 +131,52 @@ class SubjectMapper(ModelMapper):
<html lang="en">
<head>
<meta charset="UTF-8">
<title>学科</title>
<title>学科信息</title>
<style>
#container {
width: 80%;
margin: 10px auto;
}
#main>dl>dt {
font-size: 1.5em;
font-weight: bold;
}
#main>dl>dd {
font-size: 1.2em;
}
a {
text-decoration: none;
color: darkcyan;
}
</style>
</head>
<body>
<h1>所有学科</h1>
<hr>
<div id="app">
<div v-for="subject in subjects">
<h3>
<a :href="getTeachersHref(subject.no)">{{ subject.name }}</a>
<img v-if="subject.isHot" src="/static/images/hot.png" width="32">
</h3>
<p>{{ subject.intro }}</p>
<div id="container">
<h1>扣丁学堂所有学科</h1>
<hr>
<div id="main">
<dl v-for="subject in subjects">
<dt>
<a :href="'/static/html/teachers.html?sno=' + subject.no">{{ subject.name }}</a>
<img v-if="subject.is_hot" src="/static/images/hot-icon-small.png">
</dt>
<dd>{{ subject.intro }}</dd>
</dl>
</div>
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
let app = new Vue({
el: '#main',
data: {
subjects: []
},
created() {
fetch('/subjects/')
fetch('/api/subjects/')
.then(resp => resp.json())
.then(json => this.subjects = json)
},
methods: {
getTeachersHref(sno) {
return `/static/teachers.html/?sno=${sno}`
}
.then(json => {
this.subjects = json
})
}
})
</script>
@ -161,4 +186,4 @@ class SubjectMapper(ModelMapper):
前后端分离的开发需要将前端页面作为静态资源进行部署项目实际上线的时候我们会对整个Web应用进行动静分离静态资源通过Nginx或Apache服务器进行部署生成动态内容的Python程序部署在uWSGI或者Gunicorn服务器上对动态内容的请求由Nginx或Apache路由到uWSGI或Gunicorn服务器上。
在开发阶段我们通常会使用Django自带的测试服务器如果要尝试前后端分离可以先将静态页面放在之前创建的放静态资源的目录下具体的做法可以参考[项目完整代码]()。
在开发阶段我们通常会使用Django自带的测试服务器如果要尝试前后端分离可以先将静态页面放在之前创建的放静态资源的目录下具体的做法可以参考[项目完整代码](https://gitee.com/jackfrued/django19062)。

View File

@ -1,4 +1,387 @@
## RESTful架构和DRF入门
把软件Software、平台Platform、基础设施Infrastructure做成服务Service是很多IT企业都一直在做的事情这就是大家经常听到的SasS软件即服务、PasS平台即服务和IasS基础设置即服务。实现面向服务的架构SOA有诸多的方式包括RPC远程过程调用、Web Service、REST等在技术层面上SOA是一种**抽象的、松散耦合的粗粒度软件架构**在业务层面上SOA的核心概念是“**重用**”和“**互操作**”它将系统资源整合成可操作的、标准的服务使得这些资源能够被重新组合和应用。在实现SOA的诸多方案中REST被认为是最适合互联网应用的架构符合REST规范的架构也经常被称作RESTful架构。
### REST概述
REST这个词是**Roy Thomas Fielding**在他2000年的博士论文中提出的Roy是HTTP协议1.0和1.1版的主要设计者、Apache服务器软件主要作者、Apache基金会第一任主席。在他的博士论文中Roy把他对互联网软件的架构原则定名为REST即**RE**presentational **S**tate **T**ransfer的缩写中文通常翻译为“**表现层状态转移**”或“**表述状态转移**”。
这里的“表现层”其实指的是“资源”的“表现层”。所谓资源就是网络上的一个实体或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲或一种服务。我们可以用一个URI统一资源定位符指向资源要获取到这个资源访问它的URI即可URI就是资源在互联网上的唯一标识。资源可以有多种外在表现形式。我们把资源具体呈现出来的形式叫做它的“表现层”。比如文本可以用`text/plain`格式表现,也可以用`text/html`格式、`text/xml`格式、`application/json`格式表现,甚至可以采用二进制格式;图片可以用`image/jpeg`格式表现,也可以用`image/png`格式表现。URI只代表资源的实体不代表它的表现形式。严格地说有些网址最后的`.html`后缀名是不必要的因为这个后缀名表示格式属于“表现层”范畴而URI应该只代表“资源”的位置它的具体表现形式应该在HTTP请求的头信息中用`Accept`和`Content-Type`字段指定,这两个字段才是对“表现层”的描述。
访问一个网站就代表了客户端和服务器的一个互动过程。在这个过程中势必涉及到数据和状态的变化。Web应用通常使用HTTP作为其通信协议客户端想要操作服务器必须通过HTTP请求让服务器端发生“状态转移”而这种转移是建立在表现层之上的所以就是“表现层状态转移”。客户端通过HTTP的动词GET、POST、PUT或PATCH、DELETE分别对应对资源的四种基本操作其中GET用来获取资源POST用来新建资源也可以用于更新资源PUT或PATCH用来更新资源DELETE用来删除资源。
简单的说RESTful架构就是“每一个URI代表一种资源客户端通过四个HTTP动词对服务器端资源进行操作实现资源的表现层状态转移”。
我们在设计Web应用时如果需要向客户端提供资源就可以使用REST风格的URI这是实现RESTful架构的第一步。当然真正的RESTful架构并不只是URI符合REST风格更为重要的是“无状态”和“幂等性”两个词我们在后面的课程中会为大家阐述这两点。下面的例子给出了一些符合REST风格的URI供大家在设计URI时参考。
| 请求方法HTTP动词 | URI | 解释 |
| -------------------- | -------------------------- | -------------------------------------------- |
| **GET** | `/students/` | 获取所有学生 |
| **POST** | `/students/` | 新建一个学生 |
| **GET** | `/students/ID/` | 获取指定ID的学生信息 |
| **PUT** | `/students/ID/` | 更新指定ID的学生信息提供该学生的全部信息 |
| **PATCH** | `/students/ID/` | 更新指定ID的学生信息提供该学生的部分信息 |
| **DELETE** | `/students/ID/` | 删除指定ID的学生信息 |
| **GET** | `/students/ID/friends/` | 列出指定ID的学生的所有朋友 |
| **DELETE** | `/students/ID/friends/ID/` | 删除指定ID的学生的指定ID的朋友 |
### DRF使用入门
在Django项目中如果要实现REST架构即将网站的资源发布成REST风格的API接口可以使用著名的三方库`djangorestframework` 我们通常将其简称为DRF。
#### 安装和配置DRF
安装DRF。
```Shell
pip install djangorestframework
```
配置DRF。
```Python
INSTALLED_APPS = [
'rest_framework',
]
# 下面的配置根据项目需要进行设置
REST_FRAMEWORK = {
# 配置默认页面大小
# 'PAGE_SIZE': 10,
# 配置默认的分页类
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 配置异常处理器
# 'EXCEPTION_HANDLER': '...',
# 配置默认解析器
# 'DEFAULT_PARSER_CLASSES': (
# 'rest_framework.parsers.JSONParser',
# 'rest_framework.parsers.FormParser',
# 'rest_framework.parsers.MultiPartParser',
# ),
# 配置默认限流类
# 'DEFAULT_THROTTLE_CLASSES': (
# '...'
# ),
# 配置默认授权类
# 'DEFAULT_PERMISSION_CLASSES': (
# '...',
# ),
# 配置默认认证类
# 'DEFAULT_AUTHENTICATION_CLASSES': (
# '...',
# ),
}
```
#### 编写序列化器
前后端分离的开发需要后端为前端、移动端提供API数据接口而API接口通常情况下都是返回JSON格式的数据这就需要对模型对象进行序列化处理。DRF中封装了`Serializer`类和`ModelSerializer`类用于实现序列化操作,通过继承`Serializer`类或`ModelSerializer`类,我们可以自定义序列化器,用于将对象处理成字典,代码如下所示。
```Python
class SubjectSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
fields = '__all__'
```
上面的代码直接继承了`ModelSerializer`,通过`Meta`类的`model`属性指定要序列化的模型以及`fields`属性指定需要序列化的模型字段,稍后我们就可以在视图函数中使用该类来实现对`Subject`模型的序列化。
#### 编写视图函数
DRF框架支持两种实现数据接口的方式一种是FBV基于函数的视图另一种是CBV基于类的视图。我们先看看FBV的方式如何实现数据接口代码如下所示。
```Python
@api_view(('GET', ))
def show_subjects(request: HttpRequest) -> HttpResponse:
subjects = Subject.objects.all().order_by('no')
# 创建序列化器对象并指定要序列化的模型
serializer = SubjectSerializer(subjects, many=True)
# 通过序列化器的data属性获得模型对应的字典并通过创建Response对象返回JSON格式的数据
return Response(serializer.data)
```
对比上一个章节的使用`bpmapper`实现模型序列化的代码使用DRF的代码更加简单明了而且DRF本身自带了一套页面可以方便我们查看我们使用DRF定制的数据接口如下图所示。
![](res/drf-app.png)
直接使用上一节写好的页面就可以通过Vue.js把上面接口提供的学科数据渲染并展示出来此处不再进行赘述。
#### 实现老师信息数据接口
编写序列化器。
```Python
class SubjectSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
fields = ('no', 'name')
class TeacherSerializer(serializers.ModelSerializer):
class Meta:
model = Teacher
exclude = ('subject', )
```
编写视图函数。
```Python
@api_view(('GET', ))
def show_teachers(request: HttpRequest) -> HttpResponse:
try:
sno = int(request.GET.get('sno'))
subject = Subject.objects.only('name').get(no=sno)
teachers = Teacher.objects.filter(subject=subject).defer('subject').order_by('no')
subject_seri = SubjectSimpleSerializer(subject)
teacher_seri = TeacherSerializer(teachers, many=True)
return Response({'subject': subject_seri.data, 'teachers': teacher_seri.data})
except (TypeError, ValueError, Subject.DoesNotExist):
return Response(status=404)
```
配置URL映射。
```Python
urlpatterns = [
path('api/teachers/', show_teachers),
]
```
通过Vue.js渲染页面。
```Python
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>老师信息</title>
<style>
#container {
width: 80%;
margin: 10px auto;
}
.teacher {
width: 100%;
margin: 0 auto;
padding: 10px 0;
border-bottom: 1px dashed gray;
overflow: auto;
}
.teacher>div {
float: left;
}
.photo {
height: 140px;
border-radius: 75px;
overflow: hidden;
margin-left: 20px;
}
.info {
width: 75%;
margin-left: 30px;
}
.info div {
clear: both;
margin: 5px 10px;
}
.info span {
margin-right: 25px;
}
a {
text-decoration: none;
color: darkcyan;
}
</style>
</head>
<body>
<div id="container">
<h1>{{ subject.name }}学科的老师信息</h1>
<hr>
<h2 v-if="teachers.length == 0">暂无该学科老师信息</h2>
<div class="teacher" v-for="teacher in teachers">
<div class="photo">
<img :src="'/static/images/' + teacher.photo" height="140" alt="">
</div>
<div class="info">
<div>
<span><strong>姓名:{{ teacher.name }}</strong></span>
<span>性别:{{ teacher.sex | maleOrFemale }}</span>
<span>出生日期:{{ teacher.birth }}</span>
</div>
<div class="intro">{{ teacher.intro }}</div>
<div class="comment">
<a href="" @click.prevent="praise(teacher)">好评</a>&nbsp;&nbsp;(<strong>{{ teacher.good_count }}</strong>)
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="" @click.prevent="criticize(teacher)">差评</a>&nbsp;&nbsp;(<strong>{{ teacher.bad_count }}</strong>)
</div>
</div>
</div>
<a href="/">返回首页</a>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script>
let app = new Vue({
el: '#container',
data: {
subject: null,
teachers: []
},
created() {
fetch('/api/teachers/' + location.search)
.then(resp => resp.json())
.then(json => {
this.subject = json.subject
this.teachers = json.teachers
})
},
filters: {
maleOrFemale(sex) {
return sex? '男': '女'
}
},
methods: {
praise(teacher) {
fetch('/praise/?tno=' + teacher.no)
.then(resp => resp.json())
.then(json => {
if (json.code === 20000) {
teacher.good_count = json.count
} else {
alert(json.mesg)
}
})
},
criticize(teacher) {
fetch('/criticize/?tno=' + teacher.no)
.then(resp => resp.json())
.then(json => {
if (json.code === 20000) {
teacher.bad_count = json.count
} else {
alert(json.mesg)
}
})
}
}
})
</script>
</body>
</html>
```
### 前后端分离下的用户登录
之前我们提到过, HTTP是无状态的一次请求结束连接断开下次服务器再收到请求它就不知道这个请求是哪个用户发过来的。但是对于一个Web应用而言它是需要有状态管理的这样才能让服务器知道HTTP请求来自哪个用户从而判断是否允许该用户请求以及为用户提供更好的服务这个过程就是常说的**会话管理**。
之前我们做会话管理用户跟踪的方法是用户登录成功后在服务器端通过一个session对象保存用户相关数据然后把session对象的ID写入浏览器的cookie中下一次请求时HTTP请求头中携带cookie的数据服务器从HTTP请求头读取cookie中的sessionid根据这个标识符找到对应的session对象这样就能够获取到之前保存在session中的用户数据。我们刚才说过REST架构是最适合互联网应用的架构它强调了HTTP的无状态性这样才能保证应用的水平扩展能力当并发访问量增加时可以通过增加新的服务器节点来为系统扩容。显然基于session实现用户跟踪的方式需要服务器保存session对象在做水平扩展增加新的服务器节点时需要复制和同步session对象这显然是非常麻烦的。解决这个问题有两种方案一种是架设缓存服务器如Redis让多个服务器节点共享缓存服务并将session对象直接置于缓存服务器中另一种方式放弃基于session的用户跟踪使用**基于token的用户跟踪**。
基于token的用户跟踪是在用户登录成功后为用户生成身份标识并保存在浏览器本地存储localStorage、sessionStorage、cookie等这样的话服务器不需要保存用户状态从而可以很容易的做到水平扩展。基于token的用户跟踪具体流程如下
1. 用户登录时如果登录成功就按照某种方式为用户生成一个令牌token该令牌中通常包含了用户标识、过期时间等信息而且需要加密并生成指纹避免伪造或篡改令牌服务器将令牌返回给前端
2. 前端获取到服务器返回的token保存在浏览器本地存储中可以保存在`localStorage`或`sessionStorage`中对于使用Vue.js的前端项目来说还可以通过Vuex进行状态管理
3. 对于使用了前端路由的项目来说,前端每次路由跳转,可以先判断`localStroage`中有无token如果没有则跳转到登录页
4. 每次请求后端数据接口在HTTP请求头里携带token后端接口判断请求头有无token如果没有token以及token是无效的或过期的服务器统一返回401
5. 如果前端收到HTTP响应状态码401则重定向到登录页面。
通过上面的描述相信大家已经发现了基于token的用户跟踪最为关键是在用户登录成功时要为用户生成一个token作为用户的身份标识。生成token的方法很多其中一种比较成熟的解决方案是使用JSON Web Token。
#### JWT概述
JSON Web Token通常简称为JWT它是一种开放标准RFC 7519。随着RESTful架构的流行越来越多的项目使用JWT作为用户身份认证的方式。JWT相当于是三个JSON对象经过编码后用`.`分隔并组合到一起这三个JSON对象分别是头部header、载荷payload和签名signature如下图所示。
![](res/json-web-token.png)
1. 头部
```JSON
{
"alg": "HS256",
"typ": "JWT"
}
```
其中,`alg`属性表示签名的算法默认是HMAC SHA256简写成`HS256``typ`属性表示这个令牌的类型JWT中都统一书写为`JWT`。
2. 载荷
载荷部分用来存放实际需要传递的数据。JWT官方文档中规定了7个可选的字段
- iss :签发人
- exp过期时间
- sub主题
- aud受众
- nbf生效时间
- iat签发时间
- jti编号
除了官方定义的字典,我们可以根据应用的需要添加自定义的字段,如下所示。
```JSON
{
"sub": "1234567890",
"nickname": "jackfrued",
"role": "admin"
}
```
3. 签名
签名部分是对前面两部分生成一个指纹,防止数据伪造和篡改。实现签名首先需要指定一个密钥。这个密钥只有服务器才知道,不能泄露给用户。然后,使用头部指定的签名算法(默认是`HS256`),按照下面的公式产生签名。
```Python
HS256(base64Encode(header) + '.' + base64Encode(payload), secret)
```
算出签名以后,把头部、载荷、签名三个部分拼接成一个字符串,每个部分用`.`进行分隔这样一个JWT就生成好了。
#### JWT的优缺点
使用JWT的优点非常明显包括
1. 更容易实现水平扩展,因为令牌保存在浏览器中,服务器不需要做状态管理。
2. 更容易防范CSRF攻击因为在请求头中添加`localStorage`或`sessionStorage`中的token必须靠JavaScript代码完成而不是自动添加到请求头中的。
3. 可以防伪造和篡改因为JWT有签名伪造和篡改的令牌无法通过签名验证会被认定是无效的令牌。
当然任何技术不可能只有优点没有缺点JWT也有诸多缺点大家需要在使用的时候引起注意具体包括
1. 可能会遭受到XSS攻击跨站脚本攻击通过注入恶意脚本执行JavaScript代码获取到用户令牌。
2. 在令牌过期之前,无法作废已经颁发的令牌,要解决这个问题,还需要额外的中间层和代码来辅助。
3. JWT是用户的身份令牌一旦泄露任何人都可以获得该用户的所有权限。为了降低令牌被盗用后产生的风险JWT的有效期应该设置得比较短。对于一些比较重要的权限使用时应通过其他方式再次对用户进行认证例如短信验证码等。
#### 使用PyJWT生成和验证令牌
在Python代码中可以使用三方库`PyJWT`生成和验证JWT下面是安装`PyJWT`的命令。
```Bash
pip install pyjwt
```
生成令牌。
```Python
payload = {
'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1),
'userid': 10001
}
token = jwt.encode(payload, settings.SECRET_KEY).decode()
```
验证令牌。
```Python
try:
token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTQ4NzIzOTEsInVzZXJpZCI6MTAwMDF9.FM-bNxemWLqQQBIsRVvc4gq71y42I9m2zt5nlFxNHUo'
payload = jwt.decode(token, settings.SECRET_KEY)
except InvalidTokenError:
raise AuthenticationFailed('无效的令牌或令牌已经过期')
```
相信通过上面的讲解,大家已经可以自行完成对投票项目用户登录功能的修改,如果有什么疑惑,可以参考我的代码,点击[地址一](https://github.com/jackfrued/vote)或[地址二](https://gitee.com/jackfrued/vote)可以打开项目仓库的页面。

View File

@ -1,4 +1,3 @@
## RESTful架构和DRF进阶

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -1,6 +1,6 @@
## 使用Django开发商业项目
> 说明本文的部分插图来自于《Python项目开发实战》和《精通Django》这两本书中都包含了对Django框架精彩的讲解有兴趣的读者可以自行购买阅读。
> **说明**本文的部分插图来自于《Python项目开发实战》和《精通Django》这两本书中都包含了对Django框架精彩的讲解有兴趣的读者可以自行购买阅读。
### Web应用