DRF Django 学习笔记

创建第一个测试项目

安装djanago

1
pip install Django==4.2.1

创建项目

1
django-admin startproject [project_name]

可以看到项目已经创建

image

然后在pycharm打开项目

image

项目基本结构

image

  • wsgi.py - 配置WSGI兼容的Web服务站点
  • asgi.py - 配置ASGI兼容的Web服务站点
  • urls.py - 配置站点转发的路径
  • settings.py - Django项目的全局配置文件(重要)

添加项目模块

1
python manage.py startapp [module_app]

执行以下命令创建第一个模块

1
python manage.py startapp first_module

可以看到第一个模块已经创建成功

image

添加到settings.py

image

模块文件结构

image

  • views.py - 视图层,相当于Web开发中的Controller。用于拦截请求,进行逻辑处理并返回数据
  • tests.py - 用于编写测试用例
  • models.py - 模型层,用于存放与数据库实体相对应的model
  • apps.py -
  • admin.py - 一般不用

运行项目

1
python manage.py runserver [port]

使用7000端口启动项目

1
python manage.py runserver 7000

控制台

image

输入URL访问 http://127.0.0.1:7000/

image

连接数据库

连接MySQL

先安装mysql客户端

1
pip3 install mysqlclient

启动docker数据库

通过UI启动

image

或通过命令启动

image

settings.py添加数据库信息

image

如果能正常启动则说明配置成功

1
python3 manage.py runserver 8000

DRF实现登录功能

DRF概述

DRF(Django rest framework) 基于Django的封装,是Restful风格的开发框架

官方网站 https://www.django-rest-framework.org

安装

1
pip3 install djangorestframework

添加到settings.py

image

创建user和item模块

1
2
python manage.py startapp user
python manage.py startapp item

添加到settings.py

image

数据表设计

User表

1
2
3
4
5
6
7
8
9
10
11
12
from django.db import models


# Create your models here.
class User(models.Model):
id = models.AutoField(primary_key=True, null=False, unique=True)
user_name = models.CharField(null=False, max_length=255, unique=True)
is_delete = models.IntegerField()
password = models.CharField(null=False, max_length=255, unique=False)
class Meta:
db_table = "user_tbl"

Item表

1
2
3
4
5
6
7
create table `item_tbl`(
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`price` int NULL DEFAULT NULL,
`vendor_id` int NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
)ENGINE = InnoDB AUTO_INCREMENT = 4894 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

User表 - 根据class自动在mysql db创建

1
2
python manage.py makemigrations
python manage.py migrate

数据表已经创建

image

Item表 - 在数据库创建表,并自动在项目里生成对应的model class

image

运行命令生成class

  • 如果需要追加则把>​替换为>>
1
python manage.py inspectdb --database default item_tbl > item/models.py

对应的class自动生成

image

在user_tbl插入一条记录作为第一个用户

1
insert into user_tbl(user_name, is_delete, password) VALUES('test1', 0, '123456');

前端UI准备

前后端分离,使用Vue + axios + Bootstrap快速开发

UI source code https://github.com/nauvalazhar/bootstrap-4-login-page

index.html引入vue和axios

1
2
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.21"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

修改django主页

settings.py

image

添加路由和controller

urls.py

image

views.py

image

启动效果

image

添加后端数据查询接口

目的:连接到数据库查询用户表,并且校验用户密码

项目入口文件urls.py添加user路由

image

User模块下添加序列化器

*文件名必须为serializers.py

1
2
3
4
5
6
7
8
9
from user.models import User
from rest_framework import serializers


class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"

User模块下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
31
32
33
34
from django.shortcuts import render
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin


from user.models import User
from user.serializers import UserSerializer


# Create your views here.
def home_page(request):
return render(request, "index.html")


# User controller
# post - 对应db操作insert
# get - query by id
# put - update by id
# delete - delete by id
class UserMixinAPIView(GenericAPIView,
CreateModelMixin,
RetrieveModelMixin):

queryset = User.objects
serializer_class = UserSerializer

# 添加用户
# 添加user方法测试
def post(self, request):
return self.create(request)

# 查询用户
def get(self, request, pk):
return self.retrieve(request, pk)

User模块下urls.py添加路由拦截规则

image

通过postman测试get接口, user id为1

可以看到正常返回了db里的用户信息

image

目前可以看到访问api返回的是Json数据,而不是状态码

需要添加一个工具类,并且将返回结果进行封装

image

修改user/views.py

image

image

登录改造

一般业务场景下,不会用user_id进行登录,而是使用账号和密码

views.py

image

urls.py也要做相应的改动

image

可以看到同一个get url可以兼容不同的输入参数

image

添加前端返回结果

通过@click绑定点击事件

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.21"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
const app = Vue.createApp({
data() {
return {

}
},

// Method will be executed when the page initialize
created() {
// For testing
// this.loginTest();
},

// Mount your method to DOM element
mounted() {

},

// Static method
methods: {
loginTest() {
alert("Test vue.");
},

userLogin() {
var accountName = $('#account-name').val();
var accountPwd = $('#account-password').val();

var userLoginInfo = {};
userLoginInfo.accountName = accountName;
userLoginInfo.accountPwd = accountPwd;
axios.post('/user/', userLoginInfo)
.then(res => {
// debugger;
console.log(res.data);

if (res.data.status == 200) {
// TODO 登录成功,跳转到首页
alert('登录成功');
} else {
alert(res.data.msg);
}
});
}

}
})
app.mount('#loginApp')
</script>

JWT认证

Web app的会话控制

  • Cookie -> 存储在浏览器(客户端)上的小文件,用于保存用户信息的key-value文件
  • Session -> 存储在服务端,用于保存用户会话所需的属性及配置信息。如果用户过多会导致占用的内存过大
  • Token -> 用户访问API资源所需的凭证,一般由身份标识、时间戳、用户必要信息等内容组成,是一串加密的字符串

响应流程

JWT:Json web token

官网:https://jwt.io

JWT格式

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

通过.​连接成的三段字符串

JWT组成

  • 第一段:header,指头信息包含算法,字符串是由头信息进行base64加密
  • 第二段:payload,指系统自定义存储的信息,如用户名、过期时间等,并进行base64加密
  • 第三段:verify signature,指需要系统定义 “salt”,或者称为密钥

image

JWT服务端解密

获取token,例如

1
request.GET.get("token")

然后通过.​把字符串分割成三个部分

  • 第一步:用base64解密得到header​头部信息
  • 第二步:用base64解密得到系统中的info​用户数据
  • 第三步:判断 base64 (header + info + 系统密钥) 是否等于分割后的第三部分

*要注意保护好系统的密钥

安装pyjwt

1
pip install pyjwt

JWT工具类测试

Django在初始化项目的时候会自动生成一个key,可以使用这个作为密钥

也根据自定义算法生成随机字符串

image

编写工具类生成token

设置token过期时间为5分钟

image

jwt_demo.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import datetime

import jwt

from djangoDRFDemo.settings import SECRET_KEY


def create_token():
headers = {
'alg': "HS256",
'typ': "jwt"
}

payload = {
'user_id': 1,
'username': "test_user",
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1) # 定义超时时间
}

result = jwt.encode(headers=headers, payload=payload, key=SECRET_KEY, algorithm="HS256")
return result


def get_payload(token):
try:
return jwt.decode(token, SECRET_KEY,algorithms=["HS256"])
except jwt.exceptions.DecodeError:
print("token认证失败")
except jwt.exceptions.ExpiredSignatureError:
print("token已经失效了")
except jwt.exceptions.InvalidTokenError:
print("无效的token")


if __name__ == '__main__':

# 加密过程 - 生成token
token = create_token()
print("生成token:" + token)

payload = get_payload(token)
print("获取payload:" + str(payload))

# 使用旧的token测试验证过程
# 会抛出Signature has expired异常
expired_token = """eyJhbGciOiJIUzI1NiIsInR5cCI6Imp3dCJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InRlc3RfdXNlciIsImV4cCI6MTcxODUzNTcyNH0.8miAzL3pG7yMRonfR68kBJdGnUM1JksyriomfdB-zFY"""
payload = get_payload(expired_token)

运行结果

image

JWT登录功能整合

首次登录时返回token

1.添加JWT工具类

image

2.添加View方法

image

3.添加路由

image

4.测试接口

成功返回token

image

针对非登录接口添加JWT验证

1.针对url中携带token进行验证

jwt工具类添加方法

image

View中的方法也做相应的修改

image

测试

1.先登录一次拿到token

image

2.在参数中使用token,然后发送GET请求

image

控制台也会打印出token的信息

image

3.等待token过期后再次发起请求

image

控制台显示token失效

image

针对请求header中携带token的验证方式

添加工具类

image

View里做相应的修改

image

测试接口,也是先拿到token

image

JWT全局配置

如果每一个类都添加JWT验证类,不方便维护

image

在settings.py中添加全局配置

所有的url都会被拦截

image

View里做相应的修改

注释掉类里的authentication

image

需要验证的方法里面只需要添加如下代码即可

1
2
3
# 判断token状态
if not request.user.get("status"):
return ResponseMessage.UserResponse.failed("User token is invalid.")

测试

image

设置定时任务

安装APSchedule

1
pip install django_apscheduler

settings.py添加

image

执行如下命令完成DB的更改

1
2
python3 manage.py makemigrations
python3 manage.py migrate

image

会在DB中创建APScheduler所需的表

image

修改app folder中的urls.py

image

启动APP即可看到效果

代码

1
2
3
4
5
6
7
8
9
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore,register_job

scheduler = BackgroundScheduler()
scheduler.add_jobstore(DjangoJobStore(), "default")

@register_job(scheduler, 'interval', seconds=5, name='auto_hello', replace_existing=True)
def test_schedule():
print("Hello schedule")
0%