• 测试平台开发(三)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

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

  • 相关阅读:
    MongoDB简单使用
    mongodb安装部署
    分布式通信-序列化
    分布式通信协议
    分布式概念
    springboot-事件
    spring-事件
    spring-@Component/@ComponentScan注解
    springboot-Date日期时间问题
    enginx:基于openresty,一个前后端统一,生态共享的webstack实现
  • 原文地址:https://www.cnblogs.com/debugf/p/13392184.html
Copyright © 2020-2023  润新知