Django框架之DRF(武沛齐全)

一、FBV和CBV

  • FBV,function base views,其实就是编写函数来处理业务请求。
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [path('users/', views.users),
]
from django.http import JsonResponsedef users(request,*args, **kwargs):if request.method == "GET":return JsonResponse({"code":1000,"data":"xxx"})elif request.method == 'POST':return JsonResponse({"code":1000,"data":"xxx"})...
  • CBV,class base views,其实就是编写类来处理业务请求。
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [path('users/', views.UserView.as_view()),
]
from django.views import Viewclass UserView(View):def get(self, request, *args, **kwargs):return JsonResponse({"code": 1000, "data": "xxx"})def post(self, request, *args, **kwargs):return JsonResponse({"code": 1000, "data": "xxx"})

其实,CBV和FBV的底层实现本质上相同的,drf支持cbv和fbv,只不过基于drf在进行api开发时,一般都是使用cbv的形式。

二、drf项目(纯净版)

1.安装模块
pip install django
pip install djangorestframework
pip install django-redis # 按需安装
2.开启项目和api
(venv) PS D:\pythonProject\env_api> django-admin startproject drf .
(venv) PS D:\pythonProject\env_api> python manage.py startapp api

3、注册app和restframework
(1).修改settirngs.py
INSTALLED_APPS = [# 'django.contrib.admin',# 'django.contrib.auth',# 'django.contrib.contenttypes',# 'django.contrib.sessions',# 'django.contrib.messages','django.contrib.staticfiles','api.apps.ApiConfig','rest_framework',
]MIDDLEWARE = ['django.middleware.security.SecurityMiddleware',# 'django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware',# 'django.middleware.csrf.CsrfViewMiddleware',# 'django.contrib.auth.middleware.AuthenticationMiddleware',# 'django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request',# 'django.contrib.auth.context_processors.auth',# 'django.contrib.messages.context_processors.messages',],},},
]################# DRF配置 ##################
REST_FRAMEWORK = {"UNAUTHENTICATED_USER": None
}
(2).新建urls参数配置

from django.urls import path
from api import views
from api.utils import Home
urlpatterns = [path('home/', views.HomeView.as_view()),
]
(3).views.py新建视图类
from rest_framework.views import APIView
from rest_framework.response import Response# Create your views here.class HomeView(APIView):def get(self, request):return Response("...")
# 启动项目
(venv) PS D:\pythonProject\api> python manage.py runserver
(4).页面展示

三、request和参数

1.参数(示例代码详见)

2.属性

先来学一个关于面向对象的知识点。

class Request(object):def __init__(self, req, xx):self._request = reqself.xx = xxobj = Request(1, 2)
print(obj.xx)
print(obj._request)

获取对象中的成员时,本质上会调用 __getattribute__方法,默认我们不定义就用父类中的。

class Request(object):def __init__(self, req, xx):self._request = reqself.xx = xxdef __getattribute__(self, item):print("执行__getattribute__", item)return super().__getattribute__(item)obj = Request(1, 2)
print(obj.xx)
print(obj._request)
# int(obj.v1) # 报错
# 注意:如果不是对象中的成员,就会报错。

不过想要访问对象中不存在成员,则可以通过定义 __getattr__实现。

  • 先执行自己的 __getattribute__
  • 再执行父类的__getattribute__
    • 是自己对象,直接获取并返回
    • 不是自己对象,调用__getattr__
class Request(object):def __init__(self, req, xx):self._request = reqself.xx = xxdef __getattribute__(self, item):print("执行__getattribute__", item)return super().__getattribute__(item)def __getattr__(self, item):print("__getattr__", item)return 999obj = Request(1, 2)
print(obj.xx)
print(obj._request)
print(obj.v1)
3.对象封装
class HttpRequest(object):def __init__(self):passdef v1(self):print("v1")def v2(self):print("v1")class Request(object):def __init__(self,req, xx):self._request = reqself.xx = xxrequest = HttpRequest()
request.v1()
request.v2()request = Request(request,111)
request._request.v1()
request._request.v2()
class HttpRequest(object):def __init__(self):passdef v1(self):print("v1")def v2(self):print("v1")class Request(object):def __init__(self,req, xx):self._request = reqself.xx = xxdef __getattr__(self, attr):try:return getattr(self._request, attr)except AttributeError:return self.__getattribute__(attr)request = HttpRequest()
request.v1()
request.v2()request = Request(request,111)
request.v1()
request.v2()
4.源码分析

5.request对象

drf中的request其实是对请求的再次封装,其目的就是在原来的request对象基础中再进行封装一些drf中需要用到的值。

示例代码详见

四、认证

认证组件 = [认证类,认证类,认证类...] --->执行每个认证类的authenticate方法,认证成功或者失败,不会执行后续的认证类,返回None,执行后续的认证类。

接收用户请求体中发送过来的数据request.data

接收用户url传递过来的数据request.query_params

在开发API过程中,有些功能需要登录才能访问,有些无需登录。drf中的认证组件主要就是用来实现此功能。

关于认证组件,我们用案例的形式,先来学习常见的用用场景,然后再来剖析源码。

1.案例一:

项目要开发3个接口,其中1个无需登录接口、2个必须登录才能访问的接口。

在浏览器上中访问:/order/token=xxxdsfsdfdf

认证组件中返回的两个值,分别赋值给:request.user 和 request.auth。

2.案例二:

项目要开发100个接口,其中1个无需登录接口、99个必须登录才能访问的接口。

此时,就需要用到drf的全局配置(认证组件的类不能放在视图view.py中,会因为导入APIView导致循环引用)。

示例代码详见

3.案例三:

项目要开发100个接口,其中1个无需登录接口、98个必须登录才能访问的接口、1个公共接口(未登录时显示公共/已登录时显示个人信息)。

示例代码详见

4.案例四:

项目要开发100个接口,其中1个无需登录接口、98个必须登录才能访问的接口、1个公共接口(未登录时显示公共/已登录时显示个人信息)。

原来的认证信息只能放在URL中传递,如果程序中支持放在很多地方,例如:URL中、请求头中等。

认证组件中,如果是使用了多个认证类,会按照顺序逐一执行其中的authenticate方法

  • 返回None或无返回值,表示继续执行后续的认证类
  • 返回 (user, auth) 元组,则不再继续并将值赋值给request.user和request.auth
  • 抛出异常 AuthenticationFailed(...),认证失败,不再继续向后走。

示例代码详见:

5.源码分析:

6.登录(生成token)

7.认证类确认是否有这个用户

(1)直接认证失败抛出异常

(2)返回None进行下一个认证类

8.完整auth代码
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api import models
class QueryAuthentication(BaseAuthentication):def authenticate(self, request):token = request.query_params.get("token")if not token:return user_object = models.UserInfo.objects.filter(token=token).first()if user_object:return user_object, tokenraise AuthenticationFailed({"code": 3000, "error": "认证失败Query"})def authenticate_header(self, request):return "API"class HeaderAuthentication(BaseAuthentication):def authenticate(self, request):token = request.META.get("HTTP_AUTHORIZATION")if not token:returnuser_object = models.UserInfo.objects.filter(token=token).first()if user_object:return user_object, tokenraise AuthenticationFailed({"code": 3001, "error": "认证失败Header"})def authenticate_header(self, request):return "API"class NoAuthentication(BaseAuthentication):def authenticate(self, request):raise AuthenticationFailed({"status": False, "msg": "认证失败"})def authenticate_header(self, request):return "API"
9.用户登录时md5加密代码
from django.conf import settings
import hashlibdef md5(data_string):obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))obj.update(data_string.encode('utf-8'))return obj.hexdigest()

五、权限

项目中某个请求必须满足:A条件,B条件,C条件

权限组件 = [权限类,权限类,权限类...] --> 执行所有权限类的has_permission方法,返回True通过,返回False表示不通过,执行所有的权限类。默认情况下保证所有的权限类中的has_permission方法都返回True,学会源码编程进行扩展和自定义

在drf开发中,如果有些接口必须同时满足:A条件、B条件、C条件。 有些接口只需要满足:B条件、C条件,此时就可以利用权限组件来编写这些条件。

1.案例一:

(1)代码局部应用

(2)全局应用

如果全局应用后,不需要的权限接口需要配置permission_classes=[]一般情况下不在全局配置,哪个接口使用全局组件在哪个接口配置上permission_classes

2.案例二:证明是且的关系

3.案例三:修改且为或的关系

4.案例四:扩展为全局视图中使用

(1)自定义视图继承APIView

(2)继承自己的NBAPIView视图,以后继承NBAPIView就是或的关系,继承APIView就是且的关系

  • 且关系,默认支持:A条件 且 B条件 且 C条件,同时满足。
class PermissionA(BasePermission):# 权限校验失败会返回message中的信息message = {"code": 1003, 'data': "无权访问"}def has_permission(self, request, view):if request.user.role == 2:return Truereturn False# 暂时先这么写def has_object_permission(self, request, view, obj):return True
  • 或关系,自定义(方便扩展)
class APIView(View):def check_permissions(self, request):"""Check if the request should be permitted.Raises an appropriate exception if the request is not permitted."""for permission in self.get_permissions():if not permission.has_permission(request, self):self.permission_denied(request,message=getattr(permission, 'message', None),code=getattr(permission, 'code', None))

六、限流

1.限流说明:

限流,限制用户访问频率,例如:用户1分钟最多访问100次 或者 短信验证码一天每天可以发送50次, 防止盗刷。

  • 对于匿名用户,使用用户IP作为唯一标识。
  • 对于登录用户,使用用户ID或名称作为唯一标识。
缓存={用户标识:[12:33,12:32,12:31,12:30,12,]    1小时/5次   12:34   11:34
{
pip3 install django-redis
CACHES = {"default": {"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://127.0.0.1:6379","OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient","PASSWORD": "qwe123",}}
}

CACHES = {"default": {"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://127.0.0.1:6379","OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient","PASSWORD": "qwe123",}}
}
from django.urls import path, re_path
from app01 import viewsurlpatterns = [path('api/order/', views.OrderView.as_view()),
]
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import exceptions
from rest_framework import status
from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cacheclass ThrottledException(exceptions.APIException):status_code = status.HTTP_429_TOO_MANY_REQUESTSdefault_code = 'throttled'class MyRateThrottle(SimpleRateThrottle):cache = default_cache  # 访问记录存放在django的缓存中(需设置缓存)scope = "user"  # 构造缓存中的keycache_format = 'throttle_%(scope)s_%(ident)s'# 设置访问频率,例如:1分钟允许访问10次# 其他:'s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day'THROTTLE_RATES = {"user": "10/m"}def get_cache_key(self, request, view):if request.user:ident = request.user.pk  # 用户IDelse:ident = self.get_ident(request)  # 获取请求用户IP(去request中找请求头)# throttle_u # throttle_user_11.11.11.11ser_2return self.cache_format % {'scope': self.scope, 'ident': ident}def throttle_failure(self):wait = self.wait()detail = {"code": 1005,"data": "访问频率限制",'detail': "需等待{}s才能访问".format(int(wait))}raise ThrottledException(detail)class OrderView(APIView):throttle_classes = [MyRateThrottle, ]def get(self, request):return Response({"code": 0, "data": "数据..."})
2.多个限流类

本质,每个限流的类中都有一个 allow_request 方法,此方法内部可以有三种情况:

  • 返回True,表示当前限流类允许访问,继续执行后续的限流类。
  • 返回False,表示当前限流类不允许访问,继续执行后续的限流类。所有的限流类执行完毕后,读取所有不允许的限流,并计算还需等待的时间。
  • 抛出异常,表示当前限流类不允许访问,后续限流类不再执行。

3.全局配置
REST_FRAMEWORK = {"DEFAULT_THROTTLE_CLASSES":["xxx.xxx.xx.限流类", ],"DEFAULT_THROTTLE_RATES": {"user": "10/m","xx":"100/h"}
}
4.底层源码实现:

七、版本

在restful规范中要去,后端的API中需要体现版本。

1.URL的GET参数传递(*)

2.配置
REST_FRAMEWORK = {"VERSION_PARAM": "v","DEFAULT_VERSION": "v1","ALLOWED_VERSIONS": ["v1", "v2", "v3"],"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning"
}
3.源码执行流程:

4.URL路径传递(*)

5.请求头传递

6.全局配置

上述示例中,如果想要应用某种 版本 的形式,需要在每个视图类中定义类变量:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioningclass UserView(APIView):versioning_class = QueryParameterVersioning...

如果你项目比较大,需要些很多的视图类,在每一个类中都写一遍会比较麻烦,所有drf中也支持了全局配置。

REST_FRAMEWORK = {"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",  # 处理版本的类的路径"VERSION_PARAM": "version",  # URL参数传参时的key,例如:xxxx?version=v1"ALLOWED_VERSIONS": ["v1", "v2", "v3"],  # 限制支持的版本,None表示无限制"DEFAULT_VERSION": "v1",  # 默认版本
}

访问URL:

http://127.0.0.1:8000/api/users/?version=v1
http://127.0.0.1:8000/api/users/?version=v2
http://127.0.0.1:8000/api/users/?version=v3http://127.0.0.1:8000/api/admin/?version=v1
http://127.0.0.1:8000/api/admin/?version=v2
http://127.0.0.1:8000/api/admin/?version=v3http://127.0.0.1:8000/api/v1/order/
http://127.0.0.1:8000/api/v2/order/
http://127.0.0.1:8000/api/v3/order/
7.底层源码实现

8.反向生成URL

在每个版本处理的类中还定义了reverse方法,他是用来反向生成URL并携带相关的的版本信息用的,例如:

八、解析器

之前使用 request.data 获取请求体中的数据。

这个 reqeust.data 的数据怎么来的呢?其实在drf内部是由解析器,根据请求者传入的数据格式 + 请求头来进行处理。

1. JSONParser (*)

2 .FormParser

3 .MultiPartParser(*)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data"><input type="text" name="user" /><input type="file" name="img"><input type="submit" value="提交"></form>
</body>
</html>
4.FileUploadParser(*)

解析器可以设置多个,默认解析器:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, JSONParser, FormParserclass UserView(APIView):def post(self, request):print(request.content_type)print(request.data)return Response("...")

九、Serializer(*)

drf中为我们提供了Serializer,他主要有两大功能:

  • 对请求数据校验(底层调用Django的Form和ModelForm)
  • 对数据库查询到的对象进行序列化

1.数据校验

示例1:基于Serializer:

示例2:基于ModelSerializer:

from django.db import modelsclass Role(models.Model):""" 角色表 """title = models.CharField(verbose_name="名称", max_length=32)class Department(models.Model):""" 部门表 """title = models.CharField(verbose_name="名称", max_length=32)class UserInfo(models.Model):""" 用户表 """level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)username = models.CharField(verbose_name="用户名", max_length=32)password = models.CharField(verbose_name="密码", max_length=64)age = models.IntegerField(verbose_name="年龄", default=0)email = models.CharField(verbose_name="邮箱", max_length=64)token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)# 外键depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)# 多对多roles = models.ManyToManyField(verbose_name="角色", to="Role")

提示:save方法会返回新生成的数据对象。

示例3:基于ModelSerializer(含FK+M2M):

提示:save方法会返回新生成的数据对象。

十、序列化

1.通过ORM从数据库获取到的 QuerySet 或 对象 均可以被序列化为 json 格式数据。
from django.db import modelsclass Role(models.Model):""" 角色表 """title = models.CharField(verbose_name="名称", max_length=32)class Department(models.Model):""" 部门表 """title = models.CharField(verbose_name="名称", max_length=32)class UserInfo(models.Model):""" 用户表 """level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)username = models.CharField(verbose_name="用户名", max_length=32)password = models.CharField(verbose_name="密码", max_length=64)age = models.IntegerField(verbose_name="年龄", default=0)email = models.CharField(verbose_name="邮箱", max_length=64, null=True, blank=True)token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE, null=True, blank=True)roles = models.ManyToManyField(verbose_name="角色", to="Role")
2.示例1:序列化基本字段

# 切记, 如果从数据库获取的不是QuerySet对象,而是单一对象,例如:
data_object = modes.UserInfo.objects.filter(id=2).first()
ser = UserModelSerializer(instance=data_object,many=False)
print(ser.data)
3.示例2:自定义字段

4.示例3:序列化类的嵌套

5.数据校验&序列化

上述示例均属于单一功能(要么校验,要么序列化),其实当我们编写一个序列化类既可以做数据校验,也可以做序列化,例如:

from django.db import modelsclass Role(models.Model):""" 角色表 """title = models.CharField(verbose_name="名称", max_length=32)class Department(models.Model):""" 部门表 """title = models.CharField(verbose_name="名称", max_length=32)class UserInfo(models.Model):""" 用户表 """level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)username = models.CharField(verbose_name="用户名", max_length=32)password = models.CharField(verbose_name="密码", max_length=64)age = models.IntegerField(verbose_name="年龄", default=0)email = models.CharField(verbose_name="邮箱", max_length=64, null=True, blank=True)token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE, null=True, blank=True)roles = models.ManyToManyField(verbose_name="角色", to="Role")
from django.urls import path, re_path, include
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view()),
]
from django.core.validators import EmailValidator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializersfrom app01 import modelsclass DepartModelSerializer(serializers.ModelSerializer):class Meta:model = models.Departmentfields = ['id', "title"]extra_kwargs = {"id": {"read_only": False},  # 数据验证"title": {"read_only": True}  # 序列化}class RoleModelSerializer(serializers.ModelSerializer):class Meta:model = models.Rolefields = ['id', "title"]extra_kwargs = {"id": {"read_only": False},  # 数据验证"title": {"read_only": True}  # 序列化}class UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display", read_only=True)# Serializer嵌套,不是read_only,一定要自定义create和update,自定义新增和更新的逻辑。depart = DepartModelSerializer(many=False)roles = RoleModelSerializer(many=True)extra = serializers.SerializerMethodField(read_only=True)email2 = serializers.EmailField(write_only=True)# 数据校验:username、email、email2、部门、角色信息class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "depart", "roles", "extra", "email2"]extra_kwargs = {"age": {"read_only": True},"email": {"validators": [EmailValidator, ]},}def get_extra(self, obj):return 666def validate_username(self, value):return value# 新增加数据时def create(self, validated_data):""" 如果有嵌套的Serializer,在进行数据校验时,只有两种选择:1. 将嵌套的序列化设置成 read_only2. 自定义create和update方法,自定义新建和更新的逻辑注意:用户端提交数据的格式。"""depart_id = validated_data.pop('depart')['id']role_id_list = [ele['id'] for ele in validated_data.pop('roles')]# 新增用户表validated_data['depart_id'] = depart_iduser_object = models.UserInfo.objects.create(**validated_data)# 在用户表和角色表的关联表中添加对应关系user_object.roles.add(*role_id_list)return user_objectclass UserView(APIView):""" 用户管理 """def get(self, request):""" 添加用户 """queryset = models.UserInfo.objects.all()ser = UserModelSerializer(instance=queryset, many=True)return Response({"code": 0, 'data': ser.data})def post(self, request):""" 添加用户 """ser = UserModelSerializer(data=request.data)if not ser.is_valid():return Response({'code': 1006, 'data': ser.errors})ser.validated_data.pop('email2')instance = ser.save(age=18, password="123", depart_id=1)# 新增之后的一个对象(内部调用UserModelSerializer进行序列化)print(instance)# ser = UserModelSerializer(instance=instance, many=False)# ser.datareturn Response({'code': 0, 'data': ser.data})

底层源码实现:

序列化的底层源码实现有别于上述其他的组件,序列化器相关类的定义和执行都是在视图中被调用的,所以源码的分析过程可以分为:定义类、序列化、数据校验。

源码1:序列化过程

源码2:数据校验过程

十一、序列化详细(以及各种功能)

序列化数据

1.Serializer
from django.db import modelsclass Role(models.Model):title = models.CharField(verbose_name="标题", max_length=32)order = models.IntegerField(verbose_name="顺序")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import modelsclass InfoSerializer(serializers.Serializer):id = serializers.IntegerField()title = serializers.CharField()order = serializers.IntegerField()class InfoView(APIView):def get(self, request):# 1.数据库获取多条数据# queryset = models.Role.objects.all()# ser = InfoSerializer(instance=queryset, many=True)# 2.数据库获取单条数据instance = models.Role.objects.all().first()ser = InfoSerializer(instance=instance, many=False)print(type(ser.data), ser.data)return Response(ser.data)
2. ModelSerializer
from django.db import modelsclass Role(models.Model):title = models.CharField(verbose_name="标题", max_length=32)order = models.IntegerField(verbose_name="顺序")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import modelsclass InfoSerializer(serializers.ModelSerializer):class Meta:model = models.Role# fields = "__all__"# fields = ['id', 'title', 'order']exclude = ["id"]class InfoView(APIView):def get(self, request):# 1.数据库获取多条数据# queryset = models.Role.objects.all()# ser = InfoSerializer(instance=queryset, many=True)# 2.数据库获取单条数据instance = models.Role.objects.all().first()ser = InfoSerializer(instance=instance, many=False)print(type(ser.data), ser.data)return Response(ser.data)

很显然,如果要对数据表中的字段进行序列化,使用ModelModelSerializer是要比Serializer更简洁一些的。

3. 字段和参数

在ModelModelSerializer和Serializer中都可以自定义字段,并传入一些相关参数。

from django.db import modelsclass Role(models.Model):title = models.CharField(verbose_name="标题", max_length=32)order = models.IntegerField(verbose_name="顺序")class UserInfo(models.Model):name = models.CharField(verbose_name="姓名", max_length=32)gender = models.SmallIntegerField(verbose_name="性别", choices=((1, "男"), (2, "女")))role = models.ForeignKey(verbose_name="角色", to="Role", on_delete=models.CASCADE)ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import modelsclass InfoSerializer(serializers.ModelSerializer):gender = serializers.CharField(source="get_gender_display")role = serializers.CharField(source="role.title")ctime = serializers.DateTimeField(format="%Y-%m-%d")other_name = serializers.CharField(source="name")mine = serializers.SerializerMethodField()class Meta:model = models.UserInfofields = ['id', 'name', 'gender', "role", 'ctime', "other_name", "mine"]def get_mine(self, obj):return "x-x-{}".format(obj.name)class InfoView(APIView):def get(self, request):queryset = models.UserInfo.objects.all()ser = InfoSerializer(instance=queryset, many=True)print(type(ser.data), ser.data)return Response(ser.data)

4. 序列化类嵌套

主要是ORM类中对应ForeignKey 和 ManyToManyField的字段进行序列化。

  • 基于SerializerMethodField自定义方法对关联表数据进行序列化
  • 基于嵌套的序列化类实现
from django.db import modelsclass Role(models.Model):title = models.CharField(verbose_name="标题", max_length=32)order = models.IntegerField(verbose_name="顺序")class Tag(models.Model):caption = models.CharField(verbose_name="名称", max_length=32)class UserInfo(models.Model):name = models.CharField(verbose_name="姓名", max_length=32)gender = models.SmallIntegerField(verbose_name="性别", choices=((1, "男"), (2, "女")))role = models.ForeignKey(verbose_name="角色", to="Role", on_delete=models.CASCADE)ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)tags = models.ManyToManyField(verbose_name="标签", to="Tag")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import modelsclass RoleSerializer(serializers.ModelSerializer):class Meta:model = models.Role# fields = "__all__"fields = ["id", 'title']class TagSerializer(serializers.ModelSerializer):class Meta:model = models.Tagfields = "__all__"class InfoSerializer(serializers.ModelSerializer):role = RoleSerializer()tags = TagSerializer(many=True)class Meta:model = models.UserInfofields = ['id', 'name', "role", "tags"]class InfoView(APIView):def get(self, request):queryset = models.UserInfo.objects.all()ser = InfoSerializer(instance=queryset, many=True)print(type(ser.data), ser.data)return Response(ser.data)
5. 序列化类继承
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import modelsclass MySerializer(serializers.Serializer):more = serializers.SerializerMethodField()def get_more(self, obj):return "123"class InfoSerializer(serializers.ModelSerializer, MySerializer):class Meta:model = models.UserInfofields = ["id", "name", 'more']class InfoView(APIView):def get(self, request):instance = models.UserInfo.objects.all().first()ser = InfoSerializer(instance=instance, many=False)print(type(ser.data), ser.data)return Response(ser.data)
6 底层实现原理(扩展)

声明:掌握上述知识点,已经可以让你完成工作中常见的任务。接下来的知识点,只是作为扩展,可以略过。

6.1元类

对象是通过类实例化出来的。

class Foo(object):pass# 第1步:调用Foo的__new__方法创建空对象。
# 第2步:调用Foo的__init__方法对对象进行初始化。
obj = Foo()

类是谁创建的?是由type创建出来的(默认)。

class Foo(object):v1 = 123def func(self):return 666
Foo = type("Foo",(object,),{ "v1":123, "func":lambda self:666 })

定义类时加入metaclass指定当前类的创造者。

# 由type创建Foo类型
class Foo(object):pass
# 由`东西` 创建Foo类型
class Foo(object,metaclass=东西):pass

指定元类(metaclass) 来创建类。

class MyType(type):def __new__(cls, *args, **kwargs):new_cls = super().__new__(cls, *args, **kwargs)print("创建类:", new_cls)return new_clsclass Foo(metaclass=MyType):pass
class MyType(type):def __init__(self, *args, **kwargs):print("第2步:初始化类成员:", args, **kwargs)super().__init__(*args, **kwargs)def __new__(cls, *args, **kwargs):new_cls = super().__new__(cls, *args, **kwargs)print("第1步:创建类:", new_cls)return new_clsclass Foo(metaclass=MyType):v1 = 123def func(self):pass
class MyType(type):def __init__(cls, *args, **kwargs):print("第2步:初始化类成员:", args, **kwargs)super().__init__(*args, **kwargs)def __new__(cls, *args, **kwargs):new_cls = super().__new__(cls, *args, **kwargs)print("第1步:创建类:", new_cls)return new_clsdef __call__(cls, *args, **kwargs):print("第3步:创建对象&初始化对象", cls)# 1.调用自己那个类的 __new__ 方法去创建对象new_object = cls.__new__(cls, *args, **kwargs)# 2.调用你自己那个类 __init__放发去初始化cls.__init__(new_object, *args, **kwargs)return new_objectclass Foo(metaclass=MyType):v1 = 123def func(self):passobj = Foo()
6.2.实例化字段对象
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import modelsclass InfoSerializer(serializers.Serializer):id = serializers.IntegerField()title = serializers.CharField()order = serializers.IntegerField

对于上述代码,在类InfoSerializer创建之前,其内部id、title、order字段会先进行实例化对象。

而这些IntegerField、CharField等字段的继承关系如下:

class Field:_creation_counter = 0class IntegerField(Field):passclass CharField(Field):passclass DateTimeField(Field):pass

在IntegerField、CharField等字段实例化时,内部会维护一个计数器,来表示实例化的先后顺序。

class Field:_creation_counter = 0def __init__(self, *, read_only=False...):self._creation_counter = Field._creation_counterField._creation_counter += 1class IntegerField(Field):def __init__(self, **kwargs):...super().__init__(**kwargs)class CharField(Field):def __init__(self, **kwargs):...super().__init__(**kwargs)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import modelsclass InfoSerializer(serializers.Serializer):id = serializers.IntegerField()  # 对象,内部_creation_counter=0title = serializers.CharField()  # 对象,内部_creation_counter=1order = serializers.IntegerField # 对象,内部_creation_counter=2

注意:后续会通过这个计数器排序,以此来实现字段的先后执行。

6.3.序列化类的创建
class SerializerMetaclass(type):def __new__(cls, name, bases, attrs):attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)return super().__new__(cls, name, bases, attrs)
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):...class ModelSerializer(Serializer):...class RoleSerializer(serializers.ModelSerializer):gender = serializers.CharField(source="get_gender_display")class Meta:model = models.Rolefields = ["id", 'title',"gender"]

注意:父类中指定metaclass,子类也会由此metaclass来创建类。

6.4._declared_fields

在创建类之前,元类的__new__方法在类成员中添加了一个_declared_fields(类变量)。

class SerializerMetaclass(type):@classmethoddef _get_declared_fields(cls, bases, attrs):# 1.循环获取类中定义所有的成员(类变量、方法),筛选出继承自Fields的类的字段对象。# 注意:同时会将字段在当前类成员中移除fields = [(field_name, attrs.pop(field_name)) for field_name, obj in list(attrs.items())if isinstance(obj, Field)]# 2.根据字段的_creation_counter排序fields.sort(key=lambda x: x[1]._creation_counter)# Ensures a base class field doesn't override cls attrs, and maintains# field precedence when inheriting multiple parents. e.g. if there is a# class C(A, B), and A and B both define 'field', use 'field' from A.known = set(attrs)def visit(name):known.add(name)return name# 3.读取父类中的_declared_fields字段(父类先于子类创建、序列化类支持继承)base_fields = [(visit(name), f)for base in bases if hasattr(base, '_declared_fields')for name, f in base._declared_fields.items() if name not in known]# 4.将父类和子类中的字段打包返回,赋值给当前类的_declared_fieldsreturn OrderedDict(base_fields + fields)def __new__(cls, name, bases, attrs):attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)return super().__new__(cls, name, bases, attrs)
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):...class ModelSerializer(Serializer):...class RoleSerializer(serializers.ModelSerializer):gender = serializers.CharField(source="get_gender_display")class Meta:model = models.Rolefields = ["id", 'title',"gender"]

所以,当类序列化类加载完毕后,类中成员:

  • 剔除,字段对象。RoleSerializer.gender 不存在
  • 新增,_declared_fields,是OrderedDict类型且内部包含所有字段。
RoleSerializer._declared_fields = {"gender": CharField对象
}
  • 其他,保留原样。RoleSerializer.Meta
6.5.创建序列化类对象

在视图的方法,使用序列化类对 orm 获取的QuerySet或对象进行序列化时,需要先进行初始化类的对象。

class SerializerMetaclass(type):def __new__(cls, name, bases, attrs):attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)return super().__new__(cls, name, bases, attrs)
class BaseSerializer(Field):def __init__(self, instance=None, data=empty, **kwargs):self.instance = instanceif data is not empty:self.initial_data = dataself.partial = kwargs.pop('partial', False)self._context = kwargs.pop('context', {})kwargs.pop('many', None)super().__init__(**kwargs)def __new__(cls, *args, **kwargs):if kwargs.pop('many', False):# 调用 many_init 方法获取其他对象,返回return cls.many_init(*args, **kwargs)# 创建当前类的空对象,返回return super().__new__(cls, *args, **kwargs)@classmethoddef many_init(cls, *args, **kwargs):...child_serializer = cls(*args, **kwargs)list_kwargs = {'child': child_serializer,}meta = getattr(cls, 'Meta', None)list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)return list_serializer_class(*args, **list_kwargs)class Serializer(BaseSerializer, metaclass=SerializerMetaclass):...class ModelSerializer(Serializer):...class RoleSerializer(serializers.ModelSerializer):gender = serializers.CharField(source="get_gender_display")class Meta:model = models.Rolefields = ["id", 'title',"gender"]
instance = models.UserInfo.objects.all().first()# 实例化对象,内部会:先执行__new__、再执行__init__
# 第1步:__new__
# 	默认:many=True,返回ListSerializer对象; many=False,返回当前类InfoSerializer的对象。
# 第2步:__init__
#   此处就要根据__new__返回的不同对象,执行不同对象的__init__方法。
# =====> 思考题:你觉得他为什么要这么设计? <======
ser = InfoSerializer(instance=instance, many=False)# 获取序列化后的值
ser.data
6.6.序列化-当前类
class Field:def get_attribute(self, instance):# source_attrs=[]  或 source_attrs=["xx","xx","xxx"]return get_attribute(instance, self.source_attrs)class CharField(Field):def to_representation(self, value):return str(value)
class BaseSerializer(Field):@propertydef data(self):# 第2步if not hasattr(self, '_data'):if self.instance is not None and not getattr(self, '_errors', None):# 第3步:用于序列化给对象进行初始化用的。self._data = self.to_representation(self.instance)elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):# 这里是用于对请求校验时,才触发执行的。self._data = self.to_representation(self.validated_data)else:# 这个是用于给Serializer,不传对象而传入initial_data参数用的。self._data = self.get_initial()return self._dataclass Serializer(BaseSerializer, metaclass=SerializerMetaclass):@propertydef data(self):# 第1步ret = super().datareturn ReturnDict(ret, serializer=self)def to_representation(self, instance):# 第4步ret = OrderedDict()# 第5步:获取 _declared_fields 中所有非write_only字段,即:用于序列化的字段。#       如果是ModelSerializer,也会去寻找其Meta中定义的字段 + 字段的bind方法fields = self._readable_fieldsfor field in fields:try:# 第5步:调用字段对象中的 get_attribute 方法attribute = field.get_attribute(instance)except SkipField:continuecheck_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attributeif check_for_none is None:ret[field.field_name] = Noneelse:# 第6步:调用字段对象中的 to_representation 方法ret[field.field_name] = field.to_representation(attribute)return retclass ModelSerializer(Serializer):...class RoleSerializer(serializers.ModelSerializer):gender = serializers.CharField(source="get_gender_display")class Meta:model = models.Rolefields = ["id", 'title',"gender"]
instance = models.UserInfo.objects.all().first()
ser = InfoSerializer(instance=instance, many=False)# 创建InfoSerializer类的对象,获取序列化后的值
ser.data
6.7.序列化-ListSerializer
class BaseSerializer(Field):@propertydef data(self):if not hasattr(self, '_data'):if self.instance is not None and not getattr(self, '_errors', None):# 这里self._data = self.to_representation(self.instance)elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):self._data = self.to_representation(self.validated_data)else:self._data = self.get_initial()return self._dataclass ListSerializer(BaseSerializer):@propertydef data(self):ret = super().datareturn ReturnList(ret, serializer=self)def to_representation(self, data):iterable = data.all() if isinstance(data, models.Manager) else datareturn [# 循环,利用序列化类去处理每个对象self.child.to_representation(item) for item in iterable]

十一、 数据校验

对用户发来的请求数据进行校验。

1 内置校验
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializersclass InfoSerializer(serializers.Serializer):title = serializers.CharField(required=True, max_length=20, min_length=6)order = serializers.IntegerField(required=False, max_value=100, min_value=10)level = serializers.ChoiceField(choices=[("1", "高级"), (2, "中级")])class InfoView(APIView):def post(self, request):ser = InfoSerializer(data=request.data)if ser.is_valid():return Response(ser.validated_data)else:return Response(ser.errors)
2 正则校验
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from django.core.validators import RegexValidator, EmailValidatorclass InfoSerializer(serializers.Serializer):title = serializers.CharField(required=True, max_length=20, min_length=6)order = serializers.IntegerField(required=False, max_value=100, min_value=10)level = serializers.ChoiceField(choices=[("1", "高级"), (2, "中级")])# email = serializers.EmailField()email = serializers.CharField(validators=[EmailValidator(message="邮箱格式错误")])more = serializers.CharField(validators=[RegexValidator(r"\d+", message="格式错误")])code = serializers.CharField()class InfoView(APIView):def post(self, request):ser = InfoSerializer(data=request.data)if ser.is_valid():return Response(ser.validated_data)else:return Response(ser.errors)
3 钩子校验
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptionsclass InfoSerializer(serializers.Serializer):title = serializers.CharField(required=True, max_length=20, min_length=6)order = serializers.IntegerField(required=False, max_value=100, min_value=10)code = serializers.CharField()def validate_code(self, value):print(value)if len(value) > 6:raise exceptions.ValidationError("字段钩子校验失败")return valuedef validate(self, attrs):print("validate=", attrs)# api_settings.NON_FIELD_ERRORS_KEY# raise exceptions.ValidationError("全局钩子校验失败")return attrsclass InfoView(APIView):def post(self, request):ser = InfoSerializer(data=request.data)if ser.is_valid():return Response(ser.validated_data)else:return Response(ser.errors)
4 Model校验
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from api import models
from django.core.validators import RegexValidatorclass RoleSerializer(serializers.ModelSerializer):more = serializers.CharField(required=True)class Meta:model = models.Rolefields = ["title", "order", "more"]extra_kwargs = {"title": {"validators": [RegexValidator(r"\d+", message="格式错误")]},"order": {"min_value": 5},}def validate_more(self, value):return valuedef validate(self, attrs):return attrsclass InfoView(APIView):def post(self, request):ser = RoleSerializer(data=request.data)if ser.is_valid():return Response(ser.validated_data)else:return Response(ser.errors)
5 校验+保存
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from api import models
from django.core.validators import RegexValidatorclass RoleSerializer(serializers.ModelSerializer):more = serializers.CharField(required=True)class Meta:model = models.Rolefields = ["title", "order", "more"]extra_kwargs = {"title": {"validators": [RegexValidator(r"\d+", message="格式错误")]},"order": {"min_value": 5},}def validate_more(self, value):return valuedef validate(self, attrs):return attrsclass InfoView(APIView):def post(self, request):ser = RoleSerializer(data=request.data)if ser.is_valid():ser.validated_data.pop("more")instance = ser.save()  # ser.save(v1=123,v2=234)print(instance)return Response(ser.validated_data)else:return Response(ser.errors)
6 校验+保存+FK+M2M

from django.db import modelsclass Role(models.Model):title = models.CharField(verbose_name="标题", max_length=32)order = models.IntegerField(verbose_name="顺序")class Tag(models.Model):caption = models.CharField(verbose_name="名称", max_length=32)class UserInfo(models.Model):name = models.CharField(verbose_name="姓名", max_length=32)gender = models.SmallIntegerField(verbose_name="性别", choices=((1, "男"), (2, "女")))role = models.ForeignKey(verbose_name="角色", to="Role", on_delete=models.CASCADE)ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)tags = models.ManyToManyField(verbose_name="标签", to="Tag")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import exceptions
from api import models
from django.core.validators import RegexValidator
import datetimeclass UserInfoSerializer(serializers.ModelSerializer):more = serializers.CharField(required=True)class Meta:model = models.UserInfofields = ["name", "gender", "role", "tags", "more"]extra_kwargs = {"name": {"validators": [RegexValidator(r"n-\d+", message="格式错误")]},}def validate_more(self, value):return valuedef validate(self, attrs):return attrsclass InfoView(APIView):def post(self, request):ser = UserInfoSerializer(data=request.data)if ser.is_valid():ser.validated_data.pop("more")instance = ser.save(ctime=datetime.datetime.now())print(instance)# return Response(ser.validated_data)return Response("成功")else:return Response(ser.errors)
7 钩子create

当执行save时,内部会调用 create 或 update方法,如果想要自定义保存规则,也可以在此处进行处理。

8. 校验+序列化

如果一个请求,即需要做 请求校验 又需要做 序列化 ,怎么搞呢?例如:新增数据。

  • 字段,可以通过read_only 、write_only、required 来设定
  • is_valid校验
  • data调用序列化
9. 二合一

from django.db import modelsclass Role(models.Model):""" 角色表 """title = models.CharField(verbose_name="名称", max_length=32)class Department(models.Model):""" 部门表 """title = models.CharField(verbose_name="名称", max_length=32)class UserInfo(models.Model):""" 用户表 """level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)username = models.CharField(verbose_name="用户名", max_length=32)password = models.CharField(verbose_name="密码", max_length=64)age = models.IntegerField(verbose_name="年龄", default=0)email = models.CharField(verbose_name="邮箱", max_length=64, null=True, blank=True)token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE, null=True, blank=True)roles = models.ManyToManyField(verbose_name="角色", to="Role")
from django.urls import path, re_path, include
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view()),
]
from django.core.validators import EmailValidator
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializersfrom app01 import modelsclass DepartModelSerializer(serializers.ModelSerializer):class Meta:model = models.Departmentfields = ['id', "title"]extra_kwargs = {"id": {"read_only": False},  # 数据验证"title": {"read_only": True}  # 序列化}class RoleModelSerializer(serializers.ModelSerializer):class Meta:model = models.Rolefields = ['id', "title"]extra_kwargs = {"id": {"read_only": False},  # 数据验证"title": {"read_only": True}  # 序列化}class UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display", read_only=True)# Serializer嵌套,不是read_only,一定要自定义create和update,自定义新增和更新的逻辑。depart = DepartModelSerializer(many=False)roles = RoleModelSerializer(many=True)extra = serializers.SerializerMethodField(read_only=True)email2 = serializers.EmailField(write_only=True)# 数据校验:username、email、email2、部门、角色信息class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "depart", "roles", "extra", "email2"]extra_kwargs = {"age": {"read_only": True},"email": {"validators": [EmailValidator, ]},}def get_extra(self, obj):return 666def validate_username(self, value):return value# 新增加数据时def create(self, validated_data):""" 如果有嵌套的Serializer,在进行数据校验时,只有两种选择:1. 将嵌套的序列化设置成 read_only2. 自定义create和update方法,自定义新建和更新的逻辑注意:用户端提交数据的格式。"""depart_id = validated_data.pop('depart')['id']role_id_list = [ele['id'] for ele in validated_data.pop('roles')]# 新增用户表validated_data['depart_id'] = depart_iduser_object = models.UserInfo.objects.create(**validated_data)# 在用户表和角色表的关联表中添加对应关系user_object.roles.add(*role_id_list)return user_objectclass UserView(APIView):""" 用户管理 """def get(self, request):""" 添加用户 """queryset = models.UserInfo.objects.all()ser = UserModelSerializer(instance=queryset, many=True)return Response({"code": 0, 'data': ser.data})def post(self, request):""" 添加用户 """ser = UserModelSerializer(data=request.data)if not ser.is_valid():return Response({'code': 1006, 'data': ser.errors})ser.validated_data.pop('email2')instance = ser.save(age=18, password="123", depart_id=1)# 新增之后的一个对象(内部调用UserModelSerializer进行序列化)print(instance)# ser = UserModelSerializer(instance=instance, many=False)# ser.datareturn Response({'code': 0, 'data': ser.data})
10. 独立分开

在执行不同功能时,分别使用不同的序列化器来进行处理业务。

  • GET请求,返回数据(序列化A)
  • POST请求,提交数据(序列化A) + 返回数据(序列化B)。

底层源码实现:

序列化的底层源码实现有别于上述其他的组件,序列化器相关类的定义和执行都是在视图中被调用的,所以源码的分析过程可以分为:定义类、序列化、数据校验。

源码1:序列化过程

源码2:数据校验过程

十二、分页

在查看数据列表的API中,如果 数据量 比较大,肯定不能把所有的数据都展示给用户,而需要通过分页展示。

在drf中为我们提供了一些分页先关类:

BasePagination,分页基类
PageNumberPagination(BasePagination)    支持 /accounts/?page=4&page_size=100 格式的分页
LimitOffsetPagination(BasePagination)   支持 ?offset=100&limit=10 格式的分页
CursorPagination(BasePagination)        支持 上一下 & 下一页 格式的分页(不常用)
1. PageNumberPagination

2. LimitOffsetPagination

十三、初识视图

APIView`是drf中 “顶层” 的视图类,在他的内部主要实现drf基础的组件的使用,例如:版本、认证、权限、限流等。

# urls.pyfrom django.urls import path, re_path, include
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view()),path('api/users/<int:pk>/', views.UserDetailView.as_view()),# 其他分页、筛选等条件,都是要放在 ?page=1&size=9来 传参。
]
# views.pyfrom rest_framework.views import APIView
from rest_framework.response import Responseclass UserView(APIView):# 认证、权限、限流等def get(self, request):# 业务逻辑:查看列表return Response({"code": 0, 'data': "..."})def post(self, request):# 业务逻辑:新建return Response({'code': 0, 'data': "..."})class UserDetailView(APIView):# 认证、权限、限流等def get(self, request,pk):# 业务逻辑:查看某个数据的详细return Response({"code": 0, 'data': "..."})def put(self, request,pk):# 业务逻辑:全部修改return Response({'code': 0, 'data': "..."})def patch(self, request,pk):# 业务逻辑:局部修改return Response({'code': 0, 'data': "..."})def delete(self, request,pk):# 业务逻辑:删除return Response({'code': 0, 'data': "..."})
1 .APIView
  • View,django
  • APIView,drf,在请求到来时,新增了:免除csrf、请求封装、版本、认证、权限、限流的功能。
class GenericAPIView(APIView):pass # 10功能class GenericViewSet(xxxx.View-2个功能, GenericAPIView):pass # 5功能能class UserView(GenericViewSet):def get(self,request):pass

APIView是drf中 “顶层” 的视图类,在他的内部主要实现drf基础的组件的使用,例如:版本、认证、权限、限流等。

# urls.pyfrom django.urls import path, re_path, include
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view()),path('api/users/<int:pk>/', views.UserDetailView.as_view()),
]
# views.pyfrom rest_framework.views import APIView
from rest_framework.response import Responseclass UserView(APIView):# 认证、权限、限流等def get(self, request):# 业务逻辑:查看列表return Response({"code": 0, 'data': "..."})def post(self, request):# 业务逻辑:新建return Response({'code': 0, 'data': "..."})class UserDetailView(APIView):# 认证、权限、限流等def get(self, request,pk):# 业务逻辑:查看某个数据的详细return Response({"code": 0, 'data': "..."})def put(self, request,pk):# 业务逻辑:全部修改return Response({'code': 0, 'data': "..."})def patch(self, request,pk):# 业务逻辑:局部修改return Response({'code': 0, 'data': "..."})def delete(self, request,pk):# 业务逻辑:删除return Response({'code': 0, 'data': "..."})
2. GenericAPIView

GenericAPIView 继承APIView,在APIView的基础上又增加了一些功能。例如:get_queryset、get_object等。

实际在开发中一般不会直接继承它,他更多的是担任 中间人的角色,为子类提供公共功能。

# urls.pyfrom django.urls import path, re_path, include
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view()),path('api/users/<int:pk>/', views.UserDetailView.as_view()),
]
# views.pyfrom rest_framework.generics import GenericAPIView
from rest_framework.response import Responseclass UserView(GenericAPIView):queryset = models.UserInfo.objects.filter(status=True)serializer_class = 序列化类def get(self, request):queryset = self.get_queryset()ser = self.get_serializer(intance=queryset,many=True)print(ser.data)return Response({"code": 0, 'data': "..."})    

注意:最大的意义,将数据库查询、序列化类提取到类变量中,后期再提供公共的get/post/put/delete等方法,让开发者只定义类变量,自动实现增删改查。

3.GenericViewSet

GenericViewSet类中没有定义任何代码,他就是继承 ViewSetMixin 和 GenericAPIView,也就说他的功能就是将继承的两个类的功能继承到一起。

  • GenericAPIView,将数据库查询、序列化类的定义提取到类变量中,便于后期处理。
  • ViewSetMixin,将 get/post/put/delete 等方法映射到 list、create、retrieve、update、partial_update、destroy方法中,让视图不再需要两个类。
# urls.pyfrom django.urls import path, re_path, include
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destory"})),
]
# views.pyfrom rest_framework.viewsets import GenericViewSet
from rest_framework.response import Responseclass UserView(GenericViewSet):# 认证、权限、限流等queryset = models.UserInfo.objects.filter(status=True)serializer_class = 序列化类def list(self, request):# 业务逻辑:查看列表queryset = self.get_queryset()ser = self.get_serializer(intance=queryset,many=True)print(ser.data)return Response({"code": 0, 'data': "..."})def create(self, request):# 业务逻辑:新建return Response({'code': 0, 'data': "..."})def retrieve(self, request,pk):# 业务逻辑:查看某个数据的详细return Response({"code": 0, 'data': "..."})def update(self, request,pk):# 业务逻辑:全部修改return Response({'code': 0, 'data': "..."})def partial_update(self, request,pk):# 业务逻辑:局部修改return Response({'code': 0, 'data': "..."})def destory(self, request,pk):# 业务逻辑:删除return Response({'code': 0, 'data': "..."})

注意:开发中一般也很少直接去继承他,因为他也属于是 中间人类,在原来 GenericAPIView 基础上又增加了一个映射而已。

4 .五大类

在drf的为我们提供好了5个用于做 增、删、改(含局部修改)、查列表、查单个数据的5个类(需结合 GenericViewSet 使用)。

# urls.pyfrom django.urls import path, re_path, include
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destroy"})),
]
# views.pyfrom rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import (ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin, ListModelMixin
)class UserView(CreateModelMixin,RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin,ListModelMixin,GenericViewSet):# 认证、权限、限流等queryset = models.UserInfo.objects.filter(status=True)serializer_class = 序列化类

在这个5个类中已帮我们写好了 list、create、retrieve、update、partial_update、destory 方法,我们只需要在根据写 类变量:queryset、serializer_class即可。

示例1:

# urls.pyfrom django.urls import path
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view({"get": "list"})),path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve"})),
]
# views.pyfrom rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "extra"]def get_extra(self, obj):return 666class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializer

示例2:

# urls.pyfrom django.urls import path
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view({"get": "list", "post": "create"})),path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve"})),
]
# views.pyfrom rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "extra"]def get_extra(self, obj):return 666class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, GenericViewSet):queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")

示例3:

# urls.pyfrom django.urls import path
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view({"get": "list", "post": "create"})),path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"})),
]
# views.pyfrom rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "extra"]def get_extra(self, obj):return 666class UserView(mixins.ListModelMixin,mixins.RetrieveModelMixin,mixins.CreateModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,GenericViewSet):queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")def perform_update(self, serializer):serializer.save()def perform_destroy(self, instance):instance.delete()

示例4:

# urls.pyfrom django.urls import path
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view({"get": "list", "post": "create"})),path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"})),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "extra"]def get_extra(self, obj):return 666class UserView(ModelViewSet):queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")

在开发过程中使用 五大类 或 ModelViewSet 是比较常见的,并且如果他们内部的某些功能不够用,还可以进行重新某些方法进行扩展。

问题:drf中提供了这么多视图,以后那个用的比较多?

  • 接口与数据库操作无关,直接继承APIView
  • 接口背后需要对数据库进行操作,一般:ModelViewSet 或 CreateModelMixin、ListModelMixin...
- 利用钩子自定义功能。
- 重写某个写方法,实现更加完善的功能。
  • 根据自己公司的习惯,自定义 :ModelViewSet 或 CreateModelMixin、ListModelMixin...
5. 额外的
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import actionclass XXXModelSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfofields = "__all__"class XXXView(ModelViewSet):queryset = models.UserInfo.objects.all()serializer_class = XXXModelSerializer# @action(detail=False, methods=['get'], url_path="yyy/(?P<xx>\d+)/xxx")# def get_password(self, request, xx, pk=None):#     print(xx)#     return Response("...")# @action(detail=True, methods=['get'], url_path="yyy/(?P<xx>\d+)/xxx")# def set_password(self, request, xx, pk=None):#     print(xx)#     return Response("...")
6.补充:权限

在之前定义权限类时,类中可以定义两个方法:has_permission 和 has_object_permission

  • has_permission ,在请求进入视图之前就会执行。
  • has_object_permission,当视图中调用 self.get_object时就会被调用(删除、更新、查看某个对象时都会调用),一般用于检查对某个对象是否具有权限进行操作。
class PermissionA(BasePermission):message = {"code": 1003, 'data': "无权访问"}def has_permission(self, request, view):exists = request.user.roles.filter(title="员工").exists()if exists:return Truereturn Falsedef has_object_permission(self, request, view, obj):return True

所以,让我们在编写视图类时,如果是直接获取间接继承了 GenericAPIView,同时内部调用 get_object方法,这样在权限中通过 has_object_permission 就可以进行权限的处理。

十四、路由

1.在之前进行drf开发时,对于路由我们一般进行两种配置:
  • 视图继承APIView
from django.urls import path
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view()),
]

视图继承 ViewSetMixin(GenericViewSet、ModelViewSet)

from django.urls import path, re_path, include
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destory"})),
]
2.对于这种形式的路由,drf中提供了更简便的方式:
from rest_framework import routers
from app01 import viewsrouter = routers.SimpleRouter()
router.register(r'api/users', views.UserView)urlpatterns = [# 其他URL# path('xxxx/', xxxx.as_view()),
]urlpatterns += router.urls
3.也可以利用include,给URL加前缀:
from django.urls import path, include
from rest_framework import routers
from app01 import viewsrouter = routers.SimpleRouter()
router.register(r'users', views.UserView)urlpatterns = [path('api/', include((router.urls, 'app_name'), namespace='instance_name')),# 其他URL# path('forgot-password/', ForgotPasswordFormView.as_view()),
]

十五、条件搜索

如果某个API需要传递一些条件进行搜索,其实就在是URL后面通过GET传参即可,例如:

/api/users?age=19&category=12

在drf中也有相应组件可以支持条件搜索。

1. 自定义Filter

# urls.pyfrom django.urls import path
from app01 import viewsurlpatterns = [path('api/users/', views.UserView.as_view({"get": "list", "post": "create"})),path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"})),
]
# views.pyfrom rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import BaseFilterBackend
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "extra"]def get_extra(self, obj):return 666class Filter1(BaseFilterBackend):def filter_queryset(self, request, queryset, view):age = request.query_params.get('age')if not age:return querysetreturn queryset.filter(age=age)class Filter2(BaseFilterBackend):def filter_queryset(self, request, queryset, view):user_id = request.query_params.get('id')if not user_id:return querysetreturn queryset.filter(id__gt=user_id)class UserView(ModelViewSet):filter_backends = [Filter1, Filter2]queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")
2 第三方Filter

在drf开发中有一个常用的第三方过滤器:DjangoFilterBackend。

pip install django-filter

注册app:

INSTALLED_APPS = [...'django_filters',...
]

视图配置和应用(示例1):

# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["username", "age", "email", "level_text", "extra"]def get_extra(self, obj):return 666class UserView(ModelViewSet):filter_backends = [DjangoFilterBackend, ]filterset_fields = ["id", "age", "email"]queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")

视图配置和应用(示例2):

from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import FilterSet, filters
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)depart_title = serializers.CharField(source="depart.title",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]def get_extra(self, obj):return 666class MyFilterSet(FilterSet):depart = filters.CharFilter(field_name="depart__title", lookup_expr="exact")min_id = filters.NumberFilter(field_name='id', lookup_expr='gte')class Meta:model = models.UserInfofields = ["min_id", "depart"]class UserView(ModelViewSet):filter_backends = [DjangoFilterBackend, ]filterset_class = MyFilterSetqueryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")

视图配置和应用(示例3):

from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend, OrderingFilter
from django_filters import FilterSet, filters
from app01 import modelsclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)depart_title = serializers.CharField(source="depart.title",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]def get_extra(self, obj):return 666class MyFilterSet(FilterSet):# /api/users/?min_id=2  -> id>=2min_id = filters.NumberFilter(field_name='id', lookup_expr='gte')# /api/users/?name=wupeiqi  -> not ( username=wupeiqi )name = filters.CharFilter(field_name="username", lookup_expr="exact", exclude=True)# /api/users/?depart=xx     -> depart__title like %xx%depart = filters.CharFilter(field_name="depart__title", lookup_expr="contains")# /api/users/?token=true      -> "token" IS NULL# /api/users/?token=false     -> "token" IS NOT NULLtoken = filters.BooleanFilter(field_name="token", lookup_expr="isnull")# /api/users/?email=xx     -> email like xx%email = filters.CharFilter(field_name="email", lookup_expr="startswith")# /api/users/?level=2&level=1   -> "level" = 1 OR "level" = 2(必须的是存在的数据,否则报错-->内部有校验机制)# level = filters.AllValuesMultipleFilter(field_name="level", lookup_expr="exact")level = filters.MultipleChoiceFilter(field_name="level", lookup_expr="exact", choices=models.UserInfo.level_choices)# /api/users/?age=18,20     -> age in [18,20]age = filters.BaseInFilter(field_name='age', lookup_expr="in")# /api/users/?range_id_max=10&range_id_min=1    -> id BETWEEN 1 AND 10range_id = filters.NumericRangeFilter(field_name='id', lookup_expr='range')# /api/users/?ordering=id     -> order by id asc# /api/users/?ordering=-id     -> order by id desc# /api/users/?ordering=age     -> order by age asc# /api/users/?ordering=-age     -> order by age descordering = filters.OrderingFilter(fields=["id", "age"])# /api/users/?size=1     -> limit 1(自定义搜索)size = filters.CharFilter(method='filter_size', distinct=False, required=False)class Meta:model = models.UserInfofields = ["id", "min_id", "name", "depart", "email", "level", "age", 'range_id', "size", "ordering"]def filter_size(self, queryset, name, value):int_value = int(value)return queryset[0:int_value]class UserView(ModelViewSet):filter_backends = [DjangoFilterBackend, ]filterset_class = MyFilterSetqueryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")

lookup_expr有很多常见选择:

'exact': _(''),
'iexact': _(''),'contains': _('contains'),
'icontains': _('contains'),
'startswith': _('starts with'),
'istartswith': _('starts with'),
'endswith': _('ends with'),  
'iendswith': _('ends with'),'gt': _('is greater than'),
'gte': _('is greater than or equal to'),
'lt': _('is less than'),
'lte': _('is less than or equal to'),'in': _('is in'),
'range': _('is in range'),
'isnull': _(''),'regex': _('matches regex'),
'iregex': _('matches regex'),

全局配置和应用:

# settings.py 全局配置REST_FRAMEWORK = {'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend',]
}
3.内置Filter

drf源码中内置了2个filter,分别是:

  • OrderingFilter,支持排序。
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from app01 import models
from rest_framework.filters import OrderingFilterclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)depart_title = serializers.CharField(source="depart.title",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]def get_extra(self, obj):return 666class UserView(ModelViewSet):filter_backends = [OrderingFilter, ]# ?order=id# ?order=-id# ?order=ageordering_fields = ["id", "age"]queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")

SearchFilter,支持模糊搜索。

from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from app01 import models
from rest_framework.filters import SearchFilterclass UserModelSerializer(serializers.ModelSerializer):level_text = serializers.CharField(source="get_level_display",read_only=True)depart_title = serializers.CharField(source="depart.title",read_only=True)extra = serializers.SerializerMethodField(read_only=True)class Meta:model = models.UserInfofields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]def get_extra(self, obj):return 666class UserView(ModelViewSet):# ?search=武沛%齐filter_backends = [SearchFilter, ]search_fields = ["id", "username", "age"]queryset = models.UserInfo.objects.all()serializer_class = UserModelSerializerdef perform_create(self, serializer):""" 序列化:对请求的数据校验成功后,执行保存。"""serializer.save(depart_id=1, password="123")
"app01_userinfo"."id" LIKE %武沛齐% ESCAPE '\' 
OR 
"app01_userinfo"."username" LIKE %武沛齐% ESCAPE '\' 
OR 
"app01_userinfo"."age" LIKE %武沛齐% ESCAPE '\'

十六、案例(博客系统)

开发一个博客系统,包含:博客列表、详细、登录、注册、评论、点赞、发布博客

1 表结构
from django.db import modelsclass UserInfo(models.Model):username = models.CharField(verbose_name="用户名", max_length=32, db_index=True)password = models.CharField(verbose_name="密码", max_length=64)token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True,db_index=True)class Blog(models.Model):category_choices = ((1, "云计算"), (2, "Python全栈"), (3, "Go开发"))category = models.IntegerField(verbose_name="分类", choices=category_choices)image = models.CharField(verbose_name="封面", max_length=255)title = models.CharField(verbose_name="标题", max_length=32)summary = models.CharField(verbose_name="简介", max_length=256)text = models.TextField(verbose_name="博文")ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)creator = models.ForeignKey(verbose_name="创建者", to="UserInfo", on_delete=models.CASCADE)comment_count = models.PositiveIntegerField(verbose_name="评论数", default=0)favor_count = models.PositiveIntegerField(verbose_name="赞数", default=0)class Favor(models.Model):""" 赞 """blog = models.ForeignKey(verbose_name="博客", to="Blog", on_delete=models.CASCADE)user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE)create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)class Meta:constraints = [models.UniqueConstraint(fields=['blog', 'user'], name='uni_favor_blog_user')]class Comment(models.Model):""" 评论表 """blog = models.ForeignKey(verbose_name="博客", to="Blog", on_delete=models.CASCADE)user = models.ForeignKey(verbose_name="用户", to="UserInfo", on_delete=models.CASCADE)content = models.CharField(verbose_name="内容", max_length=150)create_datetime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
2. 功能
  • 博客列表路由 + 视图(时间倒序排序) + 序列化
  • 博客详细路由(PK) + 新视图或原视图 + 序列化
  • 评论列表
在URL上通过GET方式传入博客ID,根据博客ID获取相关评论。提示:请求可以单独,也可以在博客详细的请求中返回。
  • 注册输入用户信息+重复密码进行注册
  • 登录登录成功,生成TOKEN+失效日期,返回。
  • 创建评论(需登录)
在URL上通过GET方式传入博客ID + 请求体中评论信息 发送到后端API
- 认证组件,request.user
- 构造参数保存
  • 点赞(需登录)
- 赞过不能再赞
- 结合事务实现添加赞 + 赞数量更新。
  • 新建博文(需登录)


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784124.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

快速排序---算法

1、算法概念 快速排序&#xff1a;通过一趟排序将待排记录分隔成独立的两部分&#xff0c;其中一部分记录的数据均比另一部分的数据小&#xff0c;则可分别对这两部分记录继续进行排序&#xff0c;以达到震哥哥序列有序。 快速排序的最坏运行情况是O()&#xff0c;比如说顺序数…

网络基础二补充——json与http协议

五、市面上常用序列化和反序列化工具 ​ 常用的有&#xff1a;json、protobuf、xml三种方案&#xff1b; 5.1json的使用 1.安装jsoncpp库&#xff0c;是一个第三方的开发库文件&#xff1b; sudo yum install -y jsoncpp-devel2.使用json ​ 经常使用的头文件是json.h&…

【uC/OS-III篇】uC/OS-III 创建第一个任务(For STM32)

uC/OS-III 创建第一个任务&#xff08;For STM32&#xff09; 日期&#xff1a;2024-3-30 23:55&#xff0c;结尾总结了今天学习的一些小收获 本博客对应的项目源码工程 源码项目工程 1. 首先定义错误码变量 // 用于使用uC/OS函数时返回错误码 OS_ERR err; 2. 定义任务控制…

Golang | Leetcode Golang题解之第2题两数相加

题目&#xff1a; 题解&#xff1a; func addTwoNumbers(l1, l2 *ListNode) (head *ListNode) {var tail *ListNodecarry : 0for l1 ! nil || l2 ! nil {n1, n2 : 0, 0if l1 ! nil {n1 l1.Vall1 l1.Next}if l2 ! nil {n2 l2.Vall2 l2.Next}sum : n1 n2 carrysum, carry …

Vue ElementPlus Input输入框

Input 输入框 通过鼠标或键盘输入字符 input 为受控组件&#xff0c;它总会显示 Vue 绑定值。 通常情况下&#xff0c;应当处理 input 事件&#xff0c;并更新组件的绑定值&#xff08;或使用v-model&#xff09;。否则&#xff0c;输入框内显示的值将不会改变。不支持 v-mode…

【面试经典150 | 动态规划】最小路径和

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;动态规划方法二&#xff1a;空间优化 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题…

MCGS学习——水位控制

要求 插入一个水罐&#xff0c;液位最大值为37插入一个滑动输入器&#xff0c;用来调节水罐水位&#xff0c;滑动输入器最大调节为液位最大值&#xff0c;并能清楚的显示出液位情况用仪表显示水位变化情况&#xff0c;仪表最大显示设置直观清楚方便读数&#xff0c;主划线为小…

设计模式-结构型-享元模式Flyweight

享元模式的特点&#xff1a; 享元模式可以共享相同的对象&#xff0c;避免创建过多的对象实例&#xff0c;从而节省内存资源 使用场景&#xff1a; 常用于需要创建大量相似的对象的情况 享元接口类 public interface Flyweight { void operate(String extrinsicState); } 享…

如何使用极狐GitLab 自定义 Pages 根域名

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了极狐GitLab Pages …

普发Pfeiffer 真空TCP120-TCP380-TCP035-TCP600 使用手侧

普发Pfeiffer 真空TCP120-TCP380-TCP035-TCP600 使用手侧

C++进阶:多态(笔记)

目录 1. 多态相关概念1.1 简述&#xff1a;多态1.2 概念汇总与补充 2. 多态重写的底层原理2.1 虚函数存储的结构与位置2.2 重写覆盖 1. 多态相关概念 1.1 简述&#xff1a;多态 1. 什么是多态 不同的对象去做同一个行为时&#xff0c;得到的结果不同。反应到编程语言中&#…

Go语言HTTP服务实现GET和POST请求的同时支持

引言 在现代的Web开发中&#xff0c;HTTP服务是构建网络应用程序的基础。而支持GET和POST请求是其中最基本、最常见的功能之一。GET请求用于从服务器获取数据&#xff0c;而POST请求则用于向服务器提交数据。在Go语言中&#xff0c;通过标准库中的net/http包&#xff0c;我们可…

实景三维技术:开启自然资源管理的新篇章

随着科技的不断进步&#xff0c;实景三维技术已经在多个领域得到了广泛的应用。而在自然资源管理领域&#xff0c;实景三维技术更是发挥着越来越重要的作用。本文将介绍实景三维在自然资源管理领域的应用&#xff0c;探讨其带来的优势和变革。一、什么是实景三维技术&#xff1…

开源一款剪贴板跨设备共享工具

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 昨天遇到了一个很棘手的问题&#xff1a;我的手机剪贴板需要同步到另外的两台设…

HarmonyOS实战开发-一次开发,多端部署-音乐专辑

简介 基于自适应和响应式布局&#xff0c;实现一次开发、多端部署音乐专辑页面。 相关概念 一次开发&#xff0c;多端部署&#xff1a;一套代码工程&#xff0c;一次开发上架&#xff0c;多端按需部署。支撑开发者快速高效的开发支持多种终端设备形态的应用&#xff0c;实现对…

Chatgpt掘金之旅—有爱AI商业实战篇(二)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、前言&#xff1a; 成为一名商业作者是一个蕴含着无限可能的职业选择。在当下数字化的时代&#xff0c;作家们有着众多的平台可以展示和推广自己的作品。无论您是对写书、文…

商业开源MES+源码+可拖拽式数据大屏

商业开源的一套超有价值的JAVA制造执行MES系统源码 带本地部署搭建教程 教你如何在本地运行运行起来。 开发环境&#xff1a;jdk11tomcatmysql8springbootmaven 需要源码&#xff0c;私信我付费获取。 一、系统概述&#xff1a; 万界星空科技免费试用MES、开源MES、商业开…

PP-YOLOE: An evolved version of YOLO

摘要 我们在之前 PP-YOLOv2 的基础上进行了优化&#xff0c;使用 无锚 范式&#xff0c;更强大的主干和颈部配备了 CSPRepResStage 。 ET-head 和动态标签分配算法 TAL 。 1 、介绍 受 YOLOX 的启发&#xff0c;我们进一步优化了之前的工作 PP-YOLOv2 。 PP-YOLOv2 是一款高…

PHP在线客服系统源码修复版

源码简介 在线客服系统网站源码https://www.888host.cn/330.html 新增消息预知&#xff0c;消息撤回&#xff0c;消息已读未读&#xff0c; 修复需要刷新才能收到消息 修复客户来源地址 修复消息提示音 修复桌面推送提醒 搭建环境 宝塔面板 &#xff0c;Nginx1.16-1.18 …

MySQL 之 数据库操作 及 表操作

&#x1f389;欢迎大家观看AUGENSTERN_dc的文章(o゜▽゜)o☆✨✨ &#x1f389;感谢各位读者在百忙之中抽出时间来垂阅我的文章&#xff0c;我会尽我所能向的大家分享我的知识和经验&#x1f4d6; &#x1f389;希望我们在一篇篇的文章中能够共同进步&#xff01;&#xff01;&…