测试平台开发(三)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
转载请注明出处,商用请征得作者本人同意,谢谢!!!