Django rest_framework 后端接口开发 开发与用户相关的一组接口 登录注册与用户信息查询修改

Django rest_framework 后端接口开发 开发与用户相关的一组接口

Django DRF框架用起来还是有一些难度的,需要做的配置,需要导的包很多,所以需要多多练习才能掌握它的使用。此文记录了使用Django rest_famework框架开发用户模块相关接口的流程,需要注意的点,以及源码。期间重写了Django用户模型类,自定义了Django Response消息格式等。此文不过多赘述环境及各种依赖的安装过程,如果后续有需要的话再做进一步的补充。


1 项目创建 开发获取JWT Token的接口

1.1 创建项目与应用

创建项目: django-admin startproject my_project
创建应用: 进入到manage.py所在目录中,python manage.py startapp user

对项目做初步的配置:
打开项目配置文件settings.py

# 调试开启
DEBUG = True

# 允许所有IP访问
ALLOWED_HOSTS = ['*']

# 应用
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',  # rest_framework框架
    'user',  # 刚刚创建的应用
]

# 把时区先设置好
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

1.2 开发获取JWT Token的接口

因为是前后端分离的开发,为了不让接口裸奔,所以必须要和配套的安全机制一起使用。Django提供了两种Token,这里使用JWT Token,需要事先安装djangorestframework-jwt库。
最终目标是通过访问地址: http://localhost:8000/login/ 来获取JWT Token信息

1.2.1 配置应用

打开项目配置文件settings.py,在原有基础上添加以下配置:

...
# 应用
INSTALLED_APPS = [
    ...,
    'rest_framework',
    'rest_framework.authtoken',  # 新添加的
    ...,
]

# rest_framework配置
REST_FRAMEWORK = {
    # 配置默认过滤器
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
    # 配置DRF Token
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',  # JWT认证
    ),
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', # 这是接口文档需要用到的
    
    # 配置JWT
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=3),  # 过期时间 
    'JWT_AUTH_HEADER_PREFIX': 'JWT',  # 设置Token头为 JWT xxxx
    'JWT_ALLOW_REFRESH': False,  # 禁止刷新
}
1.2.2 配置接口地址

打开项目配置目录中的urls.py,添加以下代码:

from django.contrib import admin
from django.urls import path, include
from rest_framework.documentation import include_docs_urls
from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
    path('admin/', admin.site.urls),
    path('docs/', include_docs_urls(title="my_project接口文档")),  # 访问项目接口文档的地址
    path('login/', obtain_jwt_token),  # 获取JWT token
]
  • 此处需要执行迁移:python manage.py migrate
  • 然后创建一个管理员用户: python manage.py createsuperuser
  • 然后访问login/,输入用户名和密码后,就能获取到Token了。

但是这么干了之后,调用login接口返回的只有Token,这显然是不够用的,多数时候我们还需要用户的其他信息,比如电话、用户名等等,这时候就需要通过重写用户模型类+自定义返回认证消息来解决了。

1.2.3 重写用户模型类 自定义返回认证消息
1.2.3.1 重写用户模型类

打开user应用下的models.py,添加以下代码:

from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.

# 重写用户模型类 需要继承AbstractUser
class MyUser(AbstractUser):
    SEX = (
        ('男', '男'),
        ('女', '女'),
    )
    
    LEVEL = (
        ('普通会员', '普通会员'),
        ('银卡会员', '银卡会员'),
        ('金卡会员', '金卡会员'),
        ('铂金会员', '铂金会员'),
        ('钻石卡会员', '钻石卡会员'),
    )
    
    STATUS = (
        ('正常', '正常'),
        ('异常', '异常'),
    )
    
    truename = models.CharField(verbose_name="真实姓名", blank=True, max_length=50)
    mobile = models.CharField(verbose_name="手机号码", max_length=11, default="")
    sex = models.CharField(max_length=8, verbose_name="性别", choices=SEX, blank=True)
    birthday = models.DateField(blank=True, null=True)
    user_img = models.ImageField(verbose_name="头像", upload_to='user_img', default="")
    level = models.CharField(max_length=8, verbose_name="会员等级", default="普通会员", choices=LEVEL)
    status = models.CharField(max_length=8, verbose_name="状态", default="正常", choices=STATUS)

项目全局配置文件settings.py中,添加AUTH_USER_MODEL项:

...
AUTH_USER_MODEL = 'user.MyUser'  # 应用名.模型类名
...
1.2.3.2 自定义返回认证消息

user应用目录下,新建jwt_utils.py,然后添加以下代码:

"""
自定义JWT返回的认证信息
"""

def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'token': token,
        'id': user.id,
        'username': user.username,
        'email': user.email,
        'is_active': user.is_active,
        'mobile': user.mobile,
    }

项目全局配置文件settings.py中,添加JWT_RESPONSE_PAYLOAD_HANDLER项:

# rest_framework配置
REST_FRAMEWORK = {
    ...,
    'JWT_ALLOW_REFRESH': False,  # 禁止刷新
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.jwt_utils.jwt_response_payload_handler', # 自定义认证消息
}

再次请求 http://localhost:8000/login/,输入用户名和密码之后,看到的就是自定义的认证信息:
Django rest_framework 后端接口开发 开发与用户相关的一组接口 登录注册与用户信息查询修改_第1张图片
获取Token的接口至此结束,接下来就是开发用户注册的接口了。


2 用户注册接口

用户注册接口,此处我们假设接口的入参为:用户名、密码、手机号码和邮箱这四个字段,还需要对这几个字段进行校验。

2.1 定义用户注册的序列化器

user应用目录下新建serializer.py,然后添加以下代码:

import re
from pyexpat import model
from rest_framework import serializers
from .models import MyUser

# 用户注册序列化类
class MyUserRegSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyUser
        fields = ('username', 'password', 'email', 'mobile')  # 设置显示的字段
    
    def validate_username(self, username):
        # 判断用户姓名是否已注册
        if MyUser.objects.filter(username=username).count():
            raise serializers.ValidationError("用户姓名已存在,请查询")
        
        return username
    
    def validate_mobile(self, mobile):
        # 判断手机号码是否已注册
        if MyUser.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError("手机号码已存在,请查询")
        
        # 判断手机号码格式是否有误
        REGEX_MOBILE = '1[358]\d{9}$|^147\d{8}$|^176\d{8}$'

        if not re.match(REGEX_MOBILE, mobile):
            raise serializers.ValidationError("非法手机号码")
        
        return mobile
    
    def create(self, validated_data):
        # 重写create 给密码加密
        user = super().create(validated_data)
        user.set_password(validated_data['password'])
        user.save()

        return user

2.2 定义视图

打开user应用目录下的views.py,添加以下代码:

from django.shortcuts import render

# Create your views here.
from .models import MyUser
from .serializer import MyUserRegSerializer
from .custommodelviewset import CustomModelViewSet
from rest_framework.viewsets import ModelViewSet

# 在此处你可以重写ModelViewSet,然后继承自定义ModelViewSet, 让它能返回标准的自定义消息
class MyUserViewSet(ModelViewSet):
    queryset = MyUser.objects.all()
    serializer_class = MyUserRegSerializer

2.3 配置路由

配置路由(需要分别在项目全局配置目录中的urls.py与user应用下的urls.py中添加配置项):

  • 项目配置目录中的urls.py(项目的根URLS)中添加:
from django.urls import path, include

urlpatterns = [
    ...,
    path('login/', obtain_jwt_token),  # 获取JWT token
    path('user/', include('user.urls')), # 新引入包含user应用的url
]
  • user应用下的urls.py中添加:
from django.urls import path, re_path
from .views import MyUserViewSet

user_list = MyUserViewSet.as_view({
    'get': 'list',
    'post': 'create',
})

urlpatterns = [
    path('users/', user_list),
]

2.4 调用用户注册接口

访问http://localhost:8000/user/users/:
Django rest_framework 后端接口开发 开发与用户相关的一组接口 登录注册与用户信息查询修改_第2张图片
POST可以创建用户,直接提交到数据库里。那么,接下来就是用户登接口的开发了。


3 用户登录接口

所谓登录,其实不过是一个鉴权的过程。鉴权通过则登录成功,到下一步操作;否则登陆失败,进行注册或其他。前面我们已开发完成的获取Token的接口,当访问http://localhost:8000/login/的时候,需要输入用户名和密码,然后后端校验,校验成功则返回Token;现在我们想让用户通过手机号和密码也可以登录,那么就需要重写Django自带的ModelBackend来实现。

3.1 重写自定义验证类

打开user应用下的views.py,添加以下代码,增加自定义验证类CustomBackend:

from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from django.contrib.auth import authenticate, get_user_model, login, logout

myuser = get_user_model()
class CustomBackend(ModelBackend):
    """自定义用户信息验证类"""
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
        	# 此处还可以加别的验证项 如邮箱等
            myuser = MyUser.objects.get(Q(username=username) | Q(mobile=username))
            if myuser.check_password(password):
                return myuser
        except Exception as e:
            return None

3.2 在全局配置文件settings.py中添加配置项

打开项目全局配置文件settings.py,添加以下代码:

# 自定义用户认证项
AUTHENTICATION_BACKENDS = {
    'user.views.CustomBackend',
}

3.3 测试登录接口

重新访问http://localhost:8000/login/,输入电话号码与密码:
Django rest_framework 后端接口开发 开发与用户相关的一组接口 登录注册与用户信息查询修改_第3张图片
至此,使用电话和用户名都可以实现鉴权,接下来就是用户信息查询、修改和删除的接口了。


4 用户信息查询、修改和删除接口

用户注册和用户修改所需的字段是不一样的,所以需要重新定义一个用户修改的序列化类。再进行相关的权限配置即可。

4.1 创建用户信息修改的序列化类

打开user应用下的serializers.py文件,增加MyUserUpdateSerializer类:

# 用户信息修改序列化类
class MyUserUpdateSerializer(serializers.ModelSerializer):
    username = serializers.CharField(
        read_only=True,
        error_messages={
            'required': "请输入用户名",
            'blank': "用户名不允许为空",
            'min_length': "用户名长度至少3位",
        }
    )
    mobile = serializers.CharField(read_only=True)

    class Meta:
        model=MyUser
        fields=('username', 'truename', 'user_img', 'sex', 'email', 'mobile', 'level', 'status')

4.2 修改原有的视图MyUserViewSet

打开user应用下的views.py, 修改原有的视图,添加权限,并且让不同的action返回不同的序列化器。增删改需要用户身份认证,注册和登录不需要权限。
下面是整个用户模块视图的所有代码:

from tkinter import E
from django.shortcuts import render

# Create your views here.
from .models import MyUser
from .serializer import MyUserRegSerializer, MyUserUpdateSerializer
from .custommodelviewset import CustomModelViewSet
from rest_framework.viewsets import ModelViewSet

from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
# from notebook.auth.security import set_password
from django.contrib.auth import authenticate, get_user_model, login, logout

from rest_framework_extensions.cache.mixins import CacheResponseMixin
from rest_framework_jwt.authentication import JSONWebTokenAuthentication  # jwt用户认证
from rest_framework.authentication import SessionAuthentication

from rest_framework import permissions

myuser = get_user_model()
class MyUserViewSet(CacheResponseMixin, CustomModelViewSet):
    queryset = MyUser.objects.all()
    serializer_class = MyUserRegSerializer

    authentication_classes = (
        JSONWebTokenAuthentication,
        SessionAuthentication,
    )

    # 重写get_serializer_class 使不同的action返回不同的序列化器
    def get_serializer_class(self):
        if self.action == 'create':
            return MyUserRegSerializer
        elif self.action == 'retrieve':
            return MyUserUpdateSerializer
        elif self.action == 'update':
            return MyUserUpdateSerializer
        
        return MyUserUpdateSerializer
    
    # 重写get_permissions 使不同的action 需要不同的权限
    def get_permissions(self):
        if self.action == 'retrieve':
            return [permissions.IsAuthenticated()]
        elif self.action == 'update':
            return [permissions.IsAuthenticated()]
        else:
            return []

class CustomBackend(ModelBackend):
    """自定义用户信息验证类"""
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            myuser = MyUser.objects.get(Q(username=username) | Q(mobile=username))
            if myuser.check_password(password):
                return myuser
        except Exception as e:
            return None

注意: 此处使用了rest_framework的缓存机制,需要pip install drf-extensions
然后在全局配置文件settings.py中添加以下配置:

# DRF扩展
REST_FRAMEWORK_EXTENSIONS = {
    # 缓存时间s
    'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 60,
    # 缓存存储
    'DEFAULT_USE_CACHE': 'default',
}

CustomModelViewSet是一个使用了自定义的消息格式的视图,但是原来书里的代码似乎有BUG,原来的代码:

"""
使用自定义消息格式 需要重写ModelViewSet
也就是自定义ModelViewSet
"""
from rest_framework import status, viewsets
from .customresponse import CustomResponse

class CustomModelViewSet(viewsets.ModelViewSet):
    # 重写CreateModelMixin类中的create()方法
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        
        return CustomResponse(
            data=serializer.data,
            code=201,
            msg='OK',
            status=status.HTTP_201_CREATED,
            headers=headers
        )
    
    # 重写ListModelMixin类总的list()方法
    def list(self, request, *rags, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            
            return self.get_paginated_response(serializer.data)
        
        serializer = self.get_serializer(queryset, many=True)
        
        return CustomResponse(
            data=serializer.data,
            code=200,
            msg='OK',
            status=status.HTTP_200_OK
        )
    
    # 重写RetrieveModelmixin类中的retrieve()方法
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        
        return CustomResponse(
            data=serializer.data,
            code=200,
            msg='OK',
            status=status.HTTP_200_OK
        )
    
    # 重写UpdateModelMixin中的update方法
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        
        if getattr(instance, '_prefetched_objects_cache', None):
            instance._prefetched_objects_cache = {}
        
        return CustomResponse(
            data=serializer.data,
            code=200,
            msg='OK',
            status=status.HTTP_200_OK
        )
        
    # 重写DestroyModelMixin类中的destroy
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        
        return CustomResponse(
            data=[],
            code=204,
            msg='OK',
            status=status.HTTP_204_NO_CONTENT
        )
        

CustomResponse

"""
自定义消息格式
"""

from rest_framework.response import Response
from rest_framework.serializers import Serializer

class CustomResponse(Response):
    def __init__(self, data=None, code=None, msg=None, status=None, template_name=None, headers=None, exception=False, content_type=None, **kwargs):
        super().__init__(None, status=status)
        
        if isinstance(data, Serializer):
            msg = (
                '测试信息'
            )
            
            raise AssertionError(msg)
        
        self.data = {
            'code': code,
            'msg': msg,
            'data': data
        }
        
        self.data.update(kwargs)
        
        self.template_name = template_name
        self.exception = exception
        self.content_type = content_type
        
        if headers:
            for name, value in headers.items():
                self[name] = value

4.3 配置用户信息修改 更新 查询的路由

打开user应用下的urls.py,添加一下代码:

user_detail = MyUserViewSet.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy',
})

urlpatterns = [
    ...,
    path('users//', user_detail), # 新增的路由
]

4.4 接口测试

在postman中测试的结果(注意此时需要在headers中增加Authentication:JWT):
Django rest_framework 后端接口开发 开发与用户相关的一组接口 登录注册与用户信息查询修改_第4张图片
PUT和DELETE也可以在POSTman中进行测试,大家可以试一试。


你可能感兴趣的:(全栈开发,django,python,mvc,restful,后端)