测试平台开发(三)Django实现登录注册功能

在Django中通常一个功能的开发顺序为:创建应用 -> 数据库表字段设计 -> 视图函数编写 -> 应用路由 -> 全局路由 -> 测试

Django的工作流程是:

 

一、创建用户应用

打开 Tools > Run manage.py Task…,运行 startapp空格加应用名称

startapp users  # startapp:创建应用的关键字,user:为应用的名字

生成应用后一定要记得将应用名加入到settings.py文件里名为INSTALLED_APPS的列表中,遵循规范加入到列表的最后,再添加应用也一样

# settings.py
INSTALLED_APPS=[
  ……,
  'users',
]

应用目录详解

users
 ├── migrations  # 存放迁移数据表的历史文件,内容自动生成
 │    └── __init__.py  # 一个空文件,告诉 Python 该目录是一个 Python 包。
 ├── __init__.py  # 一个空文件,告诉 Python 该目录是一个 Python 包。
 ├── admin.py  # 该应用的后台管理系统配置,这里用不到
 ├── apps.py    # 该应用的一些配置,这里用不到
 ├── models.py    # 数据模块,用来定义数据表结构
 ├── tests.py    # Django提供的自动化测试功能,这里用不到
 └── views.py    # 视图模块,代码逻辑处理的主要地点,项目中大部分代码均在这里编写

 

二、编写数据模型

创建时间、修改时间和逻辑删除这些字段会经常用到,每次都写代码会很冗余,解决办法就是写一个类继承使用

1.在项目跟目录下创建utils目录,再在utils目录下创建base_models.py文件

 1 # utils/base_models.py
 2 from django.db import models
 3 
 4 
 5 class BaseModel(models.Model):
 6     """
 7     数据库表公共字段
 8     """
 9     create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', help_text='创建时间')
10     update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间', help_text='更新时间')
11     is_delete = models.BooleanField(default=False, verbose_name='逻辑删除', help_text='逻辑删除')
12 
13     class Meta:
14         # abstract = True:为抽象模型类,用于其他模型类继承,数据库迁移时不会创建ModelBase表
15         abstract = True
16         verbose_name = '公共字段表'
17         db_table = 'BaseModel'

 

2.编写用户表的模型,继承上面的类

 1 #users/models.py
 2 from django.db import models
 3 
 4 from utils.base_models import BaseModel
 5 
 6 
 7 class Users(BaseModel):
 8     id = models.AutoField(primary_key=True, verbose_name='id主键')
 9     username = models.CharField(max_length=50, unique=True, verbose_name='用户名')
10     password = models.CharField(max_length=50, verbose_name='密码')
11     email = models.EmailField(max_length=50, verbose_name='邮箱')
12 
13     def __str__(self):
14         return self.username
15 
16     class Meta:
17         db_table = 'lx_users'
18         verbose_name = '用户数据'
19         verbose_name_plural = verbose_name

迁移数据库,打开 Tools > Run manage.py Task…,依次行

makemigrations
migrate

 

三、编写视图函数

1.先把需要用的token认证机制写出来,token 这里使用Python中的 pyjwt 库

1.1 安装 pip install pyjwt

pip install pyjwt

1.2 生成token,在users目录下边创建generate_token.py文件

 1 # users/generate_token.py
 2 from datetime import datetime, timedelta
 3 import jwt
 4 
 5 from django.conf import settings
 6 
 7 def generate_jwt_token(username):
 8     token = jwt.encode({
 9         'exp': datetime.utcnow() + timedelta(days=1),
10         'iat': datetime.utcnow(),
11         'data': {
12             'username': username
13         }
14     }, settings.SECRET_KEY, algorithm='HS256')
15     return token.decode('utf-8')

1.3 认证token的装饰器,在utils目录下创建jwt_permission_required.py文件

 1 # utils/jwt_permission_required.py
 2 import jwt
 3 from django.conf import settings
 4 from django.http import JsonResponse
 5 from users.models import Users
 6 
 7 
 8 def auth_permission_required(func):
 9     def decorator(view_func):
10         def _wrapped_view(request, *args, **kwargs):
11             try:
12                 if func == "func":
13                     auth = request.META.get('HTTP_AUTHORIZATION').split()
14                 else:
15                     auth = request.request.META['HTTP_AUTHORIZATION'].split()
16             except AttributeError:
17                 return JsonResponse({"status_code": 401, "message": "没有权限"})
18             if auth[0].lower() == 'token':
19                 try:
20                     dict = jwt.decode(auth[1], settings.SECRET_KEY, algorithms=['HS256'])
21                     username = dict.get('data').get('username')
22                 except jwt.ExpiredSignatureError:
23                     return JsonResponse({"status_code": 401, "message": "token 已过期"})
24                 except jwt.InvalidTokenError:
25                     return JsonResponse({"status_code": 401, "message": "token 无效"})
26                 except Exception as e:
27                     return JsonResponse({"status_code": 401, "message": "无法获取用户对象"})
28                 try:
29                     Users.objects.get(username=username)
30                 except Users.DoesNotExist:
31                     return JsonResponse({"status_code": 401, "message": "用户不存在"})
32             else:
33                 return JsonResponse({"status_code": 401, "message": "不支持身份验证类型"})
34             return view_func(request, *args, **kwargs)
35         return _wrapped_view
36     return decorator

 

2. 返回的参数格式需要统一规范,在utils目录下创建common.py文件来放一些公共配置,写一个result字典,所有返回格式都用这个

1 # utils/common.py
2 result = {
3     "message": None,
4     "success": False,
5     "details": None
6 }

 

3.表单校验,在users目录下创建forms.py文件,用来校验用户注册时输入的数据

 1 # users/forms.py
 2 import re
 3 
 4 from django import forms
 5 from django.core.exceptions import ValidationError
 6 
 7 from users.models import Users
 8 
 9 
10 class RegisterForm(forms.Form):
11     username = forms.CharField(
12         label="用户名",
13         required=True,
14         max_length=50,
15         min_length=2,
16         error_messages={
17             "required": "用户名不能为空",
18             "max_length": "用户名最长不能超过50个字符",
19             "min_length": "用户名最小长度为2"
20         })
21     password = forms.CharField(
22         label="密码",
23         required=True,
24         max_length=50,
25         min_length=5,
26         error_messages={
27           "required": "密码不能为空",
28           "max_length": "密码最长不能超过50个字符",
29           "min_length": "密码最小长度为5"
30         })
31     r_password = forms.CharField(
32         required=True,
33         max_length=50,
34         min_length=5,
35         label="确认密码",
36         error_messages={
37             "required": "确认密码不能为空",
38             "max_length": "确认密码最长不能超过50个字符",
39             "min_length": "确认密码最小长度为5"
40         })
41     email = forms.CharField(
42         min_length=5,
43         required=True,
44         label="邮箱",
45         error_messages={
46             "required": "邮箱不能为空",
47             "max_length": "邮箱最长不能超过50个字符",
48             "min_length": "邮箱最小长度为5"
49         }
50     )
51 
52     def clean_username(self):
53         val = self.cleaned_data.get("username")
54         ret = Users.objects.filter(username=val)
55         if not ret:
56             return val
57         else:
58             raise ValidationError("该用户名已注册!")
59 
60     def clean_email(self):
61         val = self.cleaned_data.get("email")
62         if re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$', val):
63             return val
64         else:
65             raise ValidationError("邮箱格式不正确!")
66 
67     # 走完所有的校验才走clean
68     def clean(self):
69         pwd = self.cleaned_data.get("password")
70         r_pwd = self.cleaned_data.get("r_password")
71         if pwd and r_pwd:
72             if pwd != r_pwd:
73                 raise forms.ValidationError("两次密码不一致")
74         return self.cleaned_data

 

4.编写用户应用的视图函数

 1 # users/views.py
 2 from django.http import JsonResponse
 3 from django.views import View
 4 
 5 from users.forms import RegisterForm
 6 from users.generate_token import generate_jwt_token
 7 from users.models import Users
 8 from utils.jwt_permission_required import auth_permission_required
 9 from utils.common import result
10 
11 
12 class LoginView(View):
13     def post(self, request):
14         result["message"] = "登录失败"
15         result["success"] = False
16         result["details"] = None
17         json_data = request.body.decode('utf-8')
18         if json_data:
19             python_data = eval(json_data)
20             username = python_data.get('username')
21             password = python_data.get('password')
22             data = Users.objects.filter(username=username, password=password).values("id", "username").first()
23             if data:
24                 token = generate_jwt_token(username)
25                 result["message"] = "登录成功"
26                 result["success"] = True
27                 result["details"] = {"id": data["id"], "username": data["username"],"token": token}
28                 return JsonResponse(result, status=200)
29             result["details"] = "用户名或密码错误"
30             return JsonResponse(result, status=400)
31         return JsonResponse(result, status=500)
32 
33 
34 class RegisterView(View):
35     def post(self, request):
36         result["message"] = "注册失败"
37         result["success"] = False
38         result["details"] = None
39         json_data = request.body.decode('utf-8')
40         if json_data:
41             python_data = eval(json_data)
42             data = RegisterForm(python_data)
43             if data.is_valid():
44                 data.cleaned_data.pop("r_password")
45                 Users.objects.create(**data.cleaned_data)
46                 data.cleaned_data.pop("password")
47                 result["message"] = "注册成功"
48                 result["success"] = True
49                 result["details"] = data.cleaned_data
50                 return JsonResponse(result, status=200)
51             else:
52                 result["details"] = data.errors
53                 return JsonResponse(result, status=400)
54         return JsonResponse(result, status=500)
55 
56 @auth_permission_required("func")
57 def demo(request):
58     if request.method == 'GET':
59         return JsonResponse({"state": 1, "message": "token有效"})
60     else:
61         return JsonResponse({"state": 0, "message": "token无效"})

 

四、编写应用的路由

在users目录下创建urls.py文件

 1 #users/urls.py
 2 from django.urls import path
 3 
 4 from users import views
 5 
 6 urlpatterns = [
 7     path('login', views.LoginView.as_view()),
 8     path('register', views.RegisterView.as_view()),
 9     path('demo', views.demo)
10 ]

 

五、定义全局路由

1 # testplatform/urls.py
2 from django.contrib import admin
3 from django.urls import path, include
4 
5 urlpatterns = [
6     path('admin/', admin.site.urls),
7     path('api/user/', include("users.urls")),
8 ]

 

六、测试

1.注册接口

2.登录接口

3.token验证接口

  3.1 不带token

  

  3.2 带token

  

 

 

GitHub持续更新:地址https://github.com/debugf/testplatform

 

 转载请注明出处,商用请征得作者本人同意,谢谢!!!

 

posted @ 2020-07-29 16:03  班_长  阅读(915)  评论(0编辑  收藏  举报