刘沙河 刘沙河
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希

花开半夏,半夏花开
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希
  • Python基础

  • Python进阶

  • Python并发编程

  • Django

    • django 常见命令
    • Django模板母版
    • Django视图
    • Django路由系统
    • Django--orm系统-指令&属性
    • Django--orm系统 -分组聚合
    • Django-cookie&session
    • middleware中间件
    • Django 的身份认证系统
    • Django models 之字段详解
    • Django缓存系统
    • Django数据库系统
    • Django信号系统
    • Django序列化和反序列化
    • Django中admin用法
    • DRF 版本、认证、权限、限制、解析器和渲染器
      • 1.为什么要有版本控制?
      • 2.DRF提供的版本控制方案
      • 3.版本的使用
        • 3.1全局配置
        • 3.2局部配置(使用较少)
      • 1.内置的认证
      • 2.步骤
      • 1.自定义一个权限类
      • 2.权限 局部配置
      • 3.权限 全局配置
      • 1.使用自定义限制类
        • 1.1自定义一个限制类
        • 1.2限制 局部配置
        • 1.3限制 全局配置
      • 2.使用内置限制类
        • 2.1定义内置限制类
        • 2.2全局配置
      • 1.为什么要使用分页
      • 2.DRF使用分页器
        • 2.1分页模式
        • 2.2全局配置
        • 2.3局部配置
      • 3.DRF内置分页器
        • 3.1PageNumberPagination
        • 分页器
        • 视图
        • 3.2LimitOffsetPagination
        • 分页器
        • 视图
        • 3.3CursorPagination
        • 分页器
        • 视图
      • 1.查看源码
      • 2.总结
      • 1.issubset()
      • 2.语法糖setter,getter,deleter
      • 3.ORM之update_or_create()
      • 4.assert断言
      • 5.while循环测试
    • DRF(Django REST Framework)框架
  • Flask

  • 爬虫

  • Python
  • Django
bigox
2021-03-22
目录

DRF 版本、认证、权限、限制、解析器和渲染器

# 一.DRF之版本控制

# 1.为什么要有版本控制?

API版本控制允许我们在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据). DRF提供了许多不同的版本控制方案.

可能会有一些客户端因为某些原因不再维护了, 但是我们后端的接口还要不断的更新迭代, 这个时候通过版本控制返回不同的内容就是一种不错的解决方案.

# 2.DRF提供的版本控制方案

DRF提供了五种版本控制方案, 如下:

img

# 3.版本的使用

# 3.1全局配置

  1. settings.py文件中进行全局配置

除非明确设置, 否则DEFAULT_VERSIONING_CLASS值为None, 此例中的request.version将会始终返回None.

REST_FRAMEWORK = {
    # 配置默认使用的版本控制方案: URLPathVersioning
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    'DEFAULT_VERSION': 'v1',  # 默认的版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 有效的版本
    'VERSION_PARAM': 'version',  # 版本的参数名与URL conf中一致
}
1
2
3
4
5
6
7
  1. urls.py文件中:
from django.conf.urls import url
from django.contrib import admin
from bms import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),

    url(r'^(?P<version>[v1|v2]+)/book/$',   # 版本的参数名与URL conf中一致
        views.BookViewSet.as_view(actions={'get': 'list', 'post': 'create'})),
    url(r'^(?P<version>[v1|v2]+)/book/(?P<pk>\d+)$',
        views.BookViewSet.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

]
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. bms/views.py文件中:

我们在中可以通过访问request.version来获取当前请求的具体版本, 然后根据不同的版本来返回不同的内容.

  • 只要在settings.py中配置了版本信息, 在视图(bms/views.py)中就能通过request.version获取当前版本
  • get_serilaizer_class方法可以根据不同版本返回不同的序列化类
  • get_queryset方法可以根据不同的版本返回不同的数据控制

思考: 为什么可以直接用request.version拿到版本号? --> 这就是我们看源码的目的

from bms import models
from bms.modelserializers import BookModelSerializer
from rest_framework.viewsets import ModelViewSet


class BookViewSet(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

    def get_serializer_class(self):
        """不同版本 使用不同的序列化类"""
        if self.request.version == 'v1':
            return BookModelSerializer1
        return self.serializer_class

    def get_queryset(self):
        """不同的版本可以 返回不同的数据控制"""
        if self.request.version == 'v1':
            return models.Book.objects.all()[:3]
        return self.queryset.all()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 3.2局部配置(使用较少)

我们可以在一个单独的视图上设置版本控制方案. 通常, 我们==不需要这样做==, 因为在全局范围内使用一个版本控制方案更有意义. 如果我们确实需要这样做, 请使用versioning_class属性.

  1. 导入版本控制方案:from rest_framework.versioning import 版本控制方案
  2. versioning_class=版本控制方案
  3. 定义 get_queryset方法 或 get_serializer_class方法

==注意==: 版本控制方案有五种

# AcceptHeaderVersioning
# -- 将版本信息放在请求头中

URLPathVersioning
# -- 将版本信息放在URL中,如: 127.0.0.1:8000/v1/book

NamespaceVersioning
# -- 通过namespace来区分版本

HostNameVersioning
# -- 通过主机名来区分版本

QueryParameterVersioning
# -- 通过URL查询参数来区分版本 如: 127.0.0.1:8000/authors/?version=1
1
2
3
4
5
6
7
8
9
10
11
12
13
14

my_app/views.py文件中:

# 第一步
from rest_framework.versioning import URLPathVersioning
class AuthorViewSet(ModelViewSet):
    queryset = models.Author.objects.all()
    serializer_class = AuthorModelSerializer
    
    # 第二步
    versioning_class = URLPathVersioning

    # 第三步
    def get_queryset(self):
        """不同的版本可以 返回不同的数据控制量"""
        pass
    
    # 第三步
    def get_serializer_class(self):
        """不同版本 使用不同的序列化类"""
        pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

urls.py文件中:

from django.conf.urls import url
from bms import views
urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/authors/$',
        views.AuthorViewSet.as_view(actions={'get': 'list', 'post': 'create'})),
    url(r'^(?P<version>[v1|v2]+)/authors/(?P<pk>\d+)$',
        views.AuthorViewSet.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
1
2
3
4
5
6
7
8

# 二.DRF之认证

身份验证是将==传入请求==与==一组标识凭据(如请求来自的用户或其签名的令牌)==相关联的机制. 然后 权限 和 限制 组件决定是否拒绝这个请求.

简单来说:

  • 认证 -- 确定了你是谁
  • 权限 -- 确定你能不能访问某个接口
  • 限制 -- 确定你访问某个接口的频率

认证的目的: 告诉服务端你是谁

思考: 有个问题, 我们的Django和Vue项目是分离的, 它们很可能是建立在两个不同的服务器上的, 这种前后端分离的情况我们该怎样存cookie和session呢? 对于这种情况, 我们一般是通过Vue发ajax请求来把数据保存到cookie/session中的. 还有一种解决办法, 当前端请求到来时, 前端发送过来一个==token==值给后端, 后端通过查询这个token值(数据库中匹配)就可以确定你是谁了.

总结: 我们通过token值来确定前端来访问的用户是谁.

# 1.内置的认证

img

img

# 2.步骤

1.新创建一个app: auth_demo

2.在settings.py中注册auth_demo这个app

3.auth_demo/models.py中的表结构设计:

from django.db import models

# 用户表
class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    vip = models.BooleanField(default=False)
    token = models.CharField(max_length=128, null=True, blank=True)
1
2
3
4
5
6
7
8

4.二级路由

根目录下的urls.py:

from django.conf.urls import url, include

urlpatterns = [
    url(r'^users/', include('auth_demo.urls')),
]
1
2
3
4
5

auth_demo/urls.py:

from django.conf.urls import url
from auth_demo import views

urlpatterns = [
    url(r'reg/$', views.RegView.as_view()),  # 注册
    url(r'login/$', views.LoginView.as_view()),  # 登录
    url(r'test_auth/$', views.TestAuthView.as_view()),  # 测试登录认证
]
1
2
3
4
5
6
7
8

5.视图函数 auth_demo/views.py:

注册:

from rest_framework.views import APIView
from rest_framework.response import Response
from auth_demo import models


class RegView(APIView):
    """只支持注册用户"""

    def post(self, request):
        # 1.获取用户注册的数据
        name = request.data.get('name')
        pwd = request.data.get('pwd')
        re_pwd = request.data.get('re_pwd')
        if name and pwd:
            # 2.判断密码和确认密码是否一致
            if pwd == re_pwd:
                # 3.创建用户
                models.UserInfo.objects.create(name=name, pwd=pwd)
                # 4.返回响应
                return Response('注册成功')
            else:
                return Response('两次密码不一致')
        else:
            return Response('无效的参数')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

登录:

class LoginView(APIView):
    """只支持用户登录"""

    def post(self, request):
        # 1.通过request.data获取前端提交的数据
        name = request.data.get('name')
        pwd = request.data.get('pwd')
        if name and pwd:
            # 2.从数据库中进行筛选匹配
            user_obj = models.UserInfo.objects.filter(name=name, pwd=pwd).first()
            if user_obj:
                # 3.登录成功,生成token(时间戳 + Mac地址)
                import uuid
                token = uuid.uuid1().hex
                # 4.把token保存到用户表中
                user_obj.token = token
                user_obj.save()
                # 5.返回响应(包括状态码和token值)
                return Response({'error_no': 0, 'token': token})
            else:
                # 3.用户名或密码错误,登录失败
                return Response({'error_no': 1, 'error': '用户名或密码错误'})
        else:
            return Response('无效的参数')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

6.自定义认证类 auth_demo/auth.py:

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from auth_demo import models


class MyAuth(BaseAuthentication):

    def authenticate(self, request):
        # 1.通过request.query_params获取前端的url参数
        token = request.query_params.get('token')
        if token:
            # 2.如果请求的url中携带了token参数
            user_obj = models.UserInfo.objects.filter(token=token).filter()
            if user_obj:
                # 3.token是有效的
                return user_obj, token  # 必须返回一个元组: (user_obj, token) --> (request.user, request.auth)
            else:
                raise AuthenticationFailed('无效的token')
        else:
            raise AuthenticationFailed('请求的URL中必须携带token参数')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

7.局部认证 配置: auth_demo/views.py

注意: ==局部配置的优先级高于全局配置==

from auth_demo.auth import MyAuth

# 登录之后才能看到的数据接口
class TestAuthView(APIView):
    authentication_classes = [MyAuth, ]  # 配置局部认证, 全局认证在settings.py文件中配置

    def get(self, request):
        print(request.user.name)    # request.user 用户对象
        print(request.auth)         # reequest.auth 设置的token值
        return Response('这个视图里面的数据只有登录以后才能看到!')
1
2
3
4
5
6
7
8
9
10

8.全局配置: settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['auth_demo.auth.MyAuth', ],  # 认证 全局配置
}
1
2
3

# 三.DRF之权限

# 1.自定义一个权限类

auth_demo/permissions.py:

"""
自定义一个权限组件
"""
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission): # 继承BasePermission
    message = '只有VIP才能访问'

    def has_permission(self, request, view):    # 必须实现has_permission方法
        # 只有通过权限验证的用户才能访问has_permission方法
        if not request.auth:    # request.auth --> token值
            return False
        # request.user --> 当前通过token认证的用户(UserInfo表中的用户对象)
        if request.user.vip:
            # 是VIP就通过
            return True
        else:
            # 不是VIP就拒绝
            return False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2.权限 局部配置

auth_demo/views.py:

from auth_demo.auth import MyAuth
from auth_demo.permissions import MyPermission


# 登录之后才能看到的数据接口
class TestAuthView(APIView):
    authentication_classes = [MyAuth, ]  # 配置局部认证, 全局认证在settings.py文件中配置
    permission_classes = [MyPermission, ]   # 配置局部权限, 全局权限在settings.py文件中配置

    def get(self, request):
        print(request.user.name)
        print(request.auth)
        return Response('这个视图里面的数据只有登录以后才能看到!')
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3.权限 全局配置

settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['auth_demo.auth.MyAuth', ],
    'DEFAULT_PERMISSION_CLASSES': ['auth_demo.permissions.MyPermission', ]
}
1
2
3
4

# 四.DRF之限制

# 1.使用自定义限制类

DRF内置了基本的限制类,首先我们自己动手写一个限制类,熟悉下限制组件的执行过程。

# 1.1自定义一个限制类

auth_demo/throttle.py:

import time

# 访问记录
VISIT_RECORD = {}
class MyThrottle(object):

    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        # 1.拿到当前请求的ip作为VISIT_RECORD的key
        ip = request.META.get('REMOTE_ADDR')
        # 2.拿到当前请求的时间戳
        now = time.time()
        # 3.如果是请求是第一次来访问
        if ip not in VISIT_RECORD:
            # {ip:[]}
            VISIT_RECORD[ip] = []
            return True
        # 4.把当前请求的访问记录拿出来保存到一个变量(访问历史)中
        history = VISIT_RECORD[ip]
        self.history = history
        # 5.循环访问历史,把超过10秒钟的请求时间去掉
        while history and now - history[-1] > 10:
            history.pop()
        # 6.此时,history中只保存了最近10秒钟的访问记录
        if len(history) >= 3:
            # (1)history中存放了3条及以上的历史记录,拒绝访问
            return False
        else:
            # (2)history中的历史记录不到3条,存储当前历史记录
            self.history.insert(0, now)
            return True

    def wait(self):
        """告诉客户端还需要等待多久"""
        now = time.time()
        return self.history[-1] + 10 - now
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 1.2限制 局部配置

auth_demo/views.py:

from auth_demo.throttle import MyThrottle


# 登录之后才能看到的数据接口
class TestView(APIView):
    throttle_classes = [MyThrottle, ]  # 配置局部限制, 全局限制在settings.py文件中配置

    def get(self, request):
        return Response('你成功了!这个视图里面的数据只有登录以后才能看到!')
1
2
3
4
5
6
7
8
9

# 1.3限制 全局配置

settings.py:

# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['auth_demo.auth.MyAuth', ],  # 认证 全局配置
    'DEFAULT_PERMISSION_CLASSES': ['auth_demo.permissions.MyPermission', ],  # 权限 全局配置
    'DEFAULT_THROTTLE_CLASSES': ['auth_demo.throttle.MyThrottle'],  # 限制 全局配置
}
1
2
3
4
5
6

# 2.使用内置限制类

# 2.1定义内置限制类

auth_demo/throttle.py:

#使用内置限制类

from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
    scope = "throttle"

    def get_cache_key(self, request, view):
        return self.get_ident(request)
1
2
3
4
5
6
7
8

# 2.2全局配置

REST_FRAMEWORK = {
    # 内置限制类的全局配置
    "DEFAULT_THROTTLE_RATES": {
        "throttle": "5/m",  # 这里的key要与throttle.py文件中的scope="throttle"相对应
    },
}
1
2
3
4
5
6

# 五.DRF之分页

# 1.为什么要使用分页

我们的数据表中可能会有成千上万条数据, 当我们访问某张表的所有数据时,我们不大可能需要一次把所有数据都展示出来, 因为数据量很大, 对服务端的内存压力比较大并且网络传输过程中耗时也会比较大.

通常我们会希望一部分一部分去请求数据, 也就是我们常说的一页一页获取数据并展示出来.

# 2.DRF使用分页器

# 2.1分页模式

REST framework中提供了三种分页模式:

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
1

# 2.2全局配置

REST_FRAMEWORK = {
    # 默认使用的分页类
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # 默认每页的数据个数
    'PAGE_SIZE': 100
}
1
2
3
4
5
6

# 2.3局部配置

我们可以在视图类中进行局部配置

class PublisherViewSet(ModelViewSet):
    queryset = models.Publisher.objects.all()
    serializer_class = PublisherModelSerializer
    pagination_class = PageNumberPagination  # 注意不是列表(只能有一个分页模式)
1
2
3
4

# 3.DRF内置分页器

# 3.1PageNumberPagination

按页码数分页, 第n页, 每页显示m条数据.

例如: http:127.0.0.1:8000/v1/book/?page=2&size=1

# 分页器

# bms/pagination.py

from rest_framework.pagination import PageNumberPagination
class MyPageNumber(PageNumberPagination):
    page_size = 2  # 每页显示多少条
    page_size_query_param = 'size'  # URL中每页显示条数的参数
    page_query_param = 'page'  # URL中页码的参数
    max_page_size = None  # 最大页码数限制
1
2
3
4
5
6
7
8

# 视图

# bms/views.py

from bms.pagination import MyPageNumber
class BookViewSet(ModelViewSet):
    queryset = models.Book.objects.all().order_by('id')
    serializer_class = BookModelSerializer
    """
    普通分页器
    """
    pagination_class = MyPageNumber
1
2
3
4
5
6
7
8
9
10

# 3.2LimitOffsetPagination

分页, 在n位置, 向后查看m条数据.

例如: 127.0.0.1:8000/v1/book/?offset=2&limit=2

# 分页器

# bms/pagination.py

from rest_framework.pagination import LimitOffsetPagination
class MyLimitOffset(LimitOffsetPagination):
    default_limit = 1
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    max_limit = 999
1
2
3
4
5
6
7
8

# 视图

# bms/views.py

from bms.pagination import MyLimitOffset
class BookViewSet(ModelViewSet):
    queryset = models.Book.objects.all().order_by('id')
    serializer_class = BookModelSerializer
    """
    offset分页器
    """
    pagination_class = MyLimitOffset
1
2
3
4
5
6
7
8
9
10

# 3.3CursorPagination

加密分页, 把上一页和下一页的id值记住.

# 分页器

# bms/pagination.py

from rest_framework.pagination import CursorPagination
class MyCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'
    page_size = 1
    ordering = '-id'  # 重写要排序的字段
1
2
3
4
5
6
7

# 视图

# bms/views.py

from bms.pagination import MyCursorPagination
class BookViewSet(ModelViewSet):
    queryset = models.Book.objects.all().order_by('id')
    serializer_class = BookModelSerializer
    """
    加密分页器
    """
    pagination_class = MyCursorPagination
1
2
3
4
5
6
7
8
9
10

# 六.解析器和渲染器

参考资料 (opens new window)

略.

# 七.对DRF中的request对象的相关总结

# 1.查看源码

1.APIView类

img

2.1.1initialize_request方法

img

2.1.2Request类

img

2.2.1initial方法

img

# 2.总结

  • request.data -- 前端post提交的数据
  • request.query_params -- 前端页面的url参数
  • request.user -- 通过认证的用户对象
  • request.auth -- 前端发过来的token值
  • request.version -- 版本号(如: v1, v2)
  • requst.versioning_scheme -- 版本控制方案(5个)

# 八.版本,认证,权限,限制,分页 -- 源码查看方法

from rest_framework.versioning import *      # 查看 版本 源码
from rest_framework.authentication import *  # 查看 认证 源码
from rest_framework.permissions import *     # 查看 权限 源码
from rest_framework.throttling import *      # 查看 限制 源码
from rest_framework.pagination import *      # 查看 分页 源码

from django.core.handlers.wsgi import WSGIRequest   # 查看Django自己的request 源码

from rest_framework import settings # 查看settings.py文件中的配置项(版本,认证,权限,等等)
1
2
3
4
5
6
7
8
9

# 九.补充知识

# 1.issubset()

描述: issubset()方法用于判断集合的所有元素是否都包含在指定集合中, 如果是则返回True, 否则返回False.

语法:

set.issubset(set)
1

参数:

  • set -- 必需, 要比较查找的集合

返回值: 返回布尔值, 如果都包含返回True, 否则返回False.

实例说明: 判断集合x的所有元素是否都包含在集合u中.

x = {"a", "b", "c"}
y = {"f", "e", "d", "c", "b", "a"}
z = x.issubset(y)
print(z)

# 执行结果:
# True
x = {"a", "b", "c"}
y = {"f", "e", "d", "c", "b"}
z = x.issubset(y)
print(z)

# 执行结果:
# False
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.语法糖setter,getter,deleter

实例说明:

  • 例1:
class Person:
    def __init__(self, name):
        self.name = name

p1 = Person('王乃卉')
print(p1.name)

# 执行结果:
# 王乃卉
1
2
3
4
5
6
7
8
9
  • 例2:
class Person:

    def __init__(self, name):
        self.name = name

    @property   # getter -- 获取属性
    def age(self):
        print('get age called')
        return self._age

    @age.setter # setter -- 设置属性
    def age(self, value):
        print('set age called')
        if not isinstance(value, int):
            raise TypeError('Excepted an int')
        self._age = value


p2 = Person('王力宏')  # 实例化
p2.age = 19     # 设置属性
print(p2.age)   # 获取属性

##执行结果:
# set age called
# get age called
# 19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  • 例3:
class Person:

    def __init__(self, age):
        self.age = age

    @property   # getter -- 获取属性
    def age(self):
        print('get age called')
        return self._age

    @age.setter # setter -- 设置属性
    def age(self, value):
        print('set age called')
        if not isinstance(value, int):
            raise TypeError('Excepted an int')
        self._age = value


p3 = Person(19)     # 实例化,设置属性
print(p3.age)       # 获取属性

p3.age = 22         # 设置属性
print(p3.age)       # 获取属性

##执行结果:
# set age called
# get age called
# 19
# set age called
# get age called
# 22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  • 例4:
class Person:
    def __init__(self):
        self.__name = None

    @property       # 访问属性
    def name(self):
        return self.__name

    @name.setter    # 设置属性
    def name(self, value):
        self.__name = value

    @name.deleter   # 删除属性
    def name(self):
        del self.__name

p = Person()
print(p.name)     # 访问属性 --> None
p.name = '王乃卉'  # 设置属性
print(p.name)     # 访问属性 --> 王乃卉
del p.name        # 删除属性
#print(p.name)    # 访问属性 --> 报错: 对象没有该属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 3.ORM之update_or_create()

# 检查是否有这条记录,有则更新(需要defaults参数,字典类型),无则新增
models.UserToken.objects.update_or_create(user=user_obj, defaults={
    "token": access_token
})
1
2
3
4

# 4.assert断言

根据Python 官方文档解释 (opens new window) : "Assert statements are a convenient way to insert debugging assertions into a program".

语法:

assert condition
1

用来让程序测试这个condition, 如果condition为False, 则raise一个AssertionError出来. 逻辑上等同于:

if not condition:
    raise AssertionError('error_message')
1
2

实例说明:

>>> assert 1==1
>>> assert 1==0
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AssertionError

>>> assert True
>>> assert False
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AssertionError

>>> assert 1<2
>>> assert 1>2
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AssertionError
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5.while循环测试

对比以下两个例子并思考为什么执行结果会不同.

  • 例1:
lst1 = []
while lst1[-1] and lst1:
    print('这里是lst1')

# 执行结果:
# IndexError: list index out of range
1
2
3
4
5
6
  • 例2:
lst2 = []
while lst2 and lst2[-1]:
    print('这里是lst2')

# 由于lst2为空,所以不执行while循环
1
2
3
4
5
#Python#
上次更新: 2023/04/16, 18:35:33
Django中admin用法
DRF(Django REST Framework)框架

← Django中admin用法 DRF(Django REST Framework)框架→

最近更新
01
go与http代理
05-24
02
自制申威架构k8s-reloader
12-06
03
Docker Buildx 教程
12-01
更多文章>
Theme by Vdoing | Copyright © 2020-2024 小刘扎扎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式