DRF(三) 认证组件

基于权限角色的认证

三表(一对多)


五表(多对多)


六表(Django)

用户不通过角色获得权限, 直接获得权限, 第六张表为用户表和权限表的多对多关系表


auth源码解析

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/contrib/auth/__init__.py


核心代码: return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False)

Django_apps 来源: from django.apps import apps as django_apps

apps来源: /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/apps/registry.py


方法get_model来源:


app_label 的值就是 settings.AUTH_USER_MODEL,而settings对象则是有 global_settings.py里的属性

from django.conf import global_settings

class LazySettings(LazyObject):
      def configure(self, default_settings=global_settings, **options):
        """
        Called to manually configure the settings. The 'default_settings'
        parameter sets where to retrieve any unspecified values from (its
        argument must support attribute access (__getattr__)).
        """
        if self._wrapped is not empty:
            raise RuntimeError('Settings already configured.')
        holder = UserSettingsHolder(default_settings)
        for name, value in options.items():
            if not name.isupper():
                raise TypeError('Setting %r must be uppercase.' % name)
            setattr(holder, name, value)
        self._wrapped = holder

settings = LazySettings()

查看到global_settings.py 中, AUTH_USER_MODEL = 'auth.User'


/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/contrib/auth/models.py

  • User类


  • AbstractUser类, 继承 AbstractBaseUser, PermissionsMixin类


  • PermissionsMixin类

    groups: 角色多对多字段

    user_permissions:用户权限多对多字段


  • Group类

    permissions: 角色权限多对多字段


案例

创建models类

from django.db import models

# Create your models here.

# 基于Django auth(基于角色权限认证六表)的User表设计 - 扩展字段
from django.contrib.auth.models import AbstractUser


# 用户角色多对多字段 - groups(反向 user_set)
# 用户权限多对多字段 - user_permissions (反向 user_set)
# 角色权限多对多字段 - permissions (反向)
class User(AbstractUser):
    telephone = models.CharField(max_length=11)

    class Meta:
        db_table = 'auth_user'
        verbose_name = '用户'
        verbose_name_plural = '用户'

    def __str__(self):
        return self.username


class Car(models.Model):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    brand = models.IntegerField(choices=((0, '宝马'), (1, '奔驰'), (2, '迈巴赫'), (3, '路虎')), default=0)
    is_delete = models.BooleanField(default=0)

    class Meta:
        db_table = 'auth_car'
        verbose_name = '汽车'
        verbose_name_plural = '汽车'

    def __str__(self):
        return self.name

    def brandName(self):
        return self.get_brand_display()

数据库迁移完成查看是否有telephone字段



注册models

admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin


from api import models

# Register your models here.

# admin注册自定义User设置了UserAdmin就可以秘文操作密码
admin.site.register(models.User, UserAdmin)
admin.site.register(models.Car)

编写接口

  • urls.py
from django.urls import path, re_path
from api import views

urlpatterns = [
    path('cars/', views.CarModelViewSet.as_view({'get': 'list', 'post': 'create'})),
    re_path('cars/(?P<pk>.*)/$', views.CarModelViewSet.as_view(
        {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

  • serializers.py
from rest_framework.serializers import ModelSerializer
from api import models


class CarsModelSerializer(ModelSerializer):
    class Meta:
        model = models.Car
        fields = ('name', 'price', 'brand', 'brandName')
        extra_kwargs = {
            'brand': {
                'write_only': True
            }
        }

  • views.py
from rest_framework.viewsets import ModelViewSet
from api import models, serializers
from rest_framework.response import Response


class CarModelViewSet(ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')

测试orm

import os, django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoAuth.settings')
django.setup()

from api.models import User
from django.contrib.auth.models import Group, Permission

if __name__ == '__main__':
    # User表查询
    print(User.objects.get(id=2).username)

    # 查看id为2的用户的角色 <QuerySet [<Group: 管理员>]>
    print(User.objects.get(id=2).groups.all())

    #     查看id为2的用户的角色的名字
    print(User.objects.get(id=2).groups.first().name)

    #   查看id为2的用户的权限
    # <QuerySet [<Permission: api | 汽车 | Can add car>, <Permission: api | 汽车 | Can change car>, <Permission: api | 汽车 | Can delete car>]>
    print(User.objects.get(id=2).user_permissions.all())

    #     查看角色为管理员的用户 <QuerySet [<User: java>]>
    print(User.objects.filter(groups__name='管理员'))

    #     Group 表查询
    #     查看角色名  管理员
    print(Group.objects.first().name)

    #     查看角色对应的用户  <QuerySet [<User: java>]>
    print(Group.objects.first().user_set.all())

    # 查看角色对应的权限
    # <QuerySet [<Permission: api | 汽车 | Can add car>, <Permission: api | 汽车 | Can change car>, <Permission: api | 汽车 | Can delete car>]>
    print(Group.objects.first().permissions.all())

# 权限表操作
# 查询所有权限
# <QuerySet [<Permission: admin | 日志记录 | Can add log entry>, <Permission: admin | 日志记录 | Can change log entry>, <Permission: admin | 日志记录 | Can delete log entry>, <Permission: admin | 日志记录 | Can view log entry>, <Permission: api | 汽车 | Can add car>, <Permission: api | 汽车 | Can change car>, <Permission: api | 汽车 | Can delete car>, <Permission: api | 汽车 | Can view car>, <Permission: api | 用户 | Can add user>, <Permission: api | 用户 | Can change user>, <Permission: api | 用户 | Can delete user>, <Permission: api | 用户 | Can view user>, <Permission: auth | 组 | Can add group>, <Permission: auth | 组 | Can change group>, <Permission: auth | 组 | Can delete group>, <Permission: auth | 组 | Can view group>, <Permission: auth | 权限 | Can add permission>, <Permission: auth | 权限 | Can change permission>, <Permission: auth | 权限 | Can delete permission>, <Permission: auth | 权限 | Can view permission>, '...(remaining elements truncated)...']>
print(Permission.objects.all())

# 查询 汽车添加权限权限,权限表中添加权限的id为25  <QuerySet [<User: java>]>
print(Permission.objects.filter(pk=25).first().user_set.all())

# 查询汽车添加权限对应的角色,权限表中添加权限的id为25  <QuerySet [<Group: 管理员>]>
print(Permission.objects.filter(pk=25).first().group_set.all())

添加drf 权限认证配置

源码查看配置字段

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/rest_framework/settings.py


源码认证解析

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/rest_framework/views.py


  • dispatch 方法


  • initial 方法


  • check_permissions 方法


  • get_permissions 方法


  • permission_classes 属性


  • DEFAULT_PERMISSION_CLASSES 属性

    /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/rest_framework/settings.py


settings.py

  • 自定义认证类,重写authenticate方法(通过 返回 auth|user|None, 失败 抛异常)

    源码路径: `/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/sitepackages/rest_framework/authentication.py


  • 自定义权限类 重写has_permission方法 (通过True | 失败 False)

    源码路径: /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/rest_framework/permissions.py 下的has_permission方法

# drf权限配置
REST_FRAMEWORK = {
    # 自定义认证类, 重写authenticate方法(通过 返回 auth|user|None, 失败 抛异常
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # 前台sessionid 和 后台 django_session 完成认证, 赋值给request.user
        # 'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ],
    # 自定义权限类, 重写has_permission方法 (通过True | 失败 False
    'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.AllowAny',  # 所有人都能登录, 前面的校验就没用了
        # 校验request.user
        # 'rest_framework.permissions.IsAuthenticated',
        #     查操作不校验request.user, 赠改删校验request.user
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
        #     是否是在职用户(request.user.is_staff), 校验is_staff字段是否为True
        'rest_framework.permissions.IsAdminUser',
    ]

为什么 自定义认证类, 重写authenticate方法


为什么 自定义权限类, 重写has_permission方法


浏览器访问 http://127.0.0.1:8000/api/cars/,弹出登录界面


为什么一个页面登录了也会跳出来登录界面

因为在认证阶段没有用session作为认证, 所以即便是页面登录了也会跳出来需要登录的界面


自定义权限认证

在应用目录下新建permissions.py

from rest_framework.permissions import BasePermission


class isSuperUserBasePermission(BasePermission):
    def has_permission(self, request, view):
        return request.user and request.user.is_superuser


class isGroupAdminBasePermission(BasePermission):
    def has_permission(self, request, view):
        return request.user and request.user.groups.filter(name='管理员')

全局校验

settings.py

# drf权限配置
REST_FRAMEWORK = {
    # 自定义认证类, 重写authenticate方法(通过 返回 auth|user|None, 失败 抛异常
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # 前台sessionid 和 后台 django_session 完成认证, 赋值给request.user
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ],
    # 自定义权限类, 重写has_permission方法 (通过True | 失败 False
    'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.AllowAny',  # 所有人都能登录, 前面的校验就没用了
        # 校验request.user
        # 'rest_framework.permissions.IsAuthenticated',
        #     查操作不校验request.user, 赠改删校验request.user
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
        #     是否是在职用户(request.user.is_staff), 校验is_staff字段是否为True
        'rest_framework.permissions.IsAdminUser',
        #     自定义是否是超级管理员 (request.user.is_superuser)
        # 'api.permissions.isSuperUserBasePermission',
        # 自定义是否是管理员组员 (request.user.groups.filter(name='管理员'))
        'api.permissions.isGroupAdminBasePermission',

    ],
}

局部校验

views.py

from rest_framework.viewsets import ModelViewSet
from api import models, serializers
from rest_framework.response import Response
from api import permissions


class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')

jwt(json web tokens)

django默认签发token

Django 默认的请求是要通过rest_framework.authentication.SessionAuthentication 认证, 产生request.user的值

这个过程中,客户端通过nginx请求到后台服务器,第一次登录,后台服务器将session记录写入数据库;第二次登录,后台服务器查询数据库验证得到user,存放到request.user这个属性中,有多少次登录, 后台服务器就有多少次和数据库之间的IO操作

jwt

组成

头.体.签名

头:{公司基本信息,加密方式} =》 base64加密

载荷:{用户信息,过期时间} =》 base64加密

签名:{头,载荷,密钥} =〉 hash256加密


签发Token

登录的用户,过期时间,服务器密钥 + 基础信息们


  • django中服务器的密钥


校验Token

前台的头 + 前台的载荷 + 服务器密钥


DRF-JWT

django提供的jwt

 pip3 install djangorestframework-jwt

路由

主urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('api.urls')),
    # 登录接口
    path('login/', obtain_jwt_token),
]


视图

views.py局部校验

from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated


class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')

postman测试返回token值


自定义JWT登录

路由

主urls.py

from django.contrib import admin
from django.urls import path, include
from api import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('api.urls')),
    # 登录接口
    path('login/', views.loginAPIView.as_view()),
]

视图

views.py

from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.serializers import jwt_payload_handler
from rest_framework_jwt.serializers import jwt_encode_handler
from django.contrib import auth
# 自定义jwt登录
class loginAPIView(APIView):
    # jwtPayloadHandler = api_settings.JWT_PAYLOAD_HANDLER
    # jwtEncodeHandler = api_settings.JWT_ENCODE_HANDLER

    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')
        if not (username and password):
            return Response({
                'error': 'username与password必须存在'
            })

        # 第一版代码
        # userObj = models.User.objects.filter(username=username, is_active=True).first()  # type: models.User
        # print(userObj)
        # print(userObj.check_password(password))
        # if not (userObj and userObj.check_password(password)):
        #     return Response({
        #         'error': 'username与password有误'
        #     })

        # 第二版代码
        userObj = auth.authenticate(username=username, is_active=True, password=password)
        if not userObj:
            return Response({
                        'error': 'username与password有误'
                    })
        #        签发token
        payload = jwt_payload_handler(userObj)
        token = jwt_encode_handler(payload)

        return Response({
            'status': 200,
            'msg': 'ok',
            'token': token,

        })

postman测试


jwt签发token源码

jwt配置参考源码

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/rest_framework_jwt/settings.py


settings.py

# jwt配置
import datetime

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=3000),
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    authentication_classes = [JSONWebTokenAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')

postman测试

  • 登录接口获取Token


  • 汽车接口加入header Authorization: jwt <token>


如果不加入 header Authorization: jwt <token>


自定义报错信息

当登录认证的token过期时候, 会弹出如下报错

这个时候如果要将这个报错自定义成中文, 就需要重新authentications.py 这个文件里的authenticate方法, 复制/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/rest_framework_jwt/authentication.py到自己的应用目录下, 更名为authentications.py


authentications.py

import jwt
from django.utils.encoding import smart_text

from rest_framework import exceptions
from rest_framework_jwt.settings import api_settings
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework.authentication import (
    get_authorization_header
)
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER


class jwtAuthentication(BaseJSONWebTokenAuthentication):
    def get_jwt_value(self, request):
        auth = get_authorization_header(request).split()
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

        if not auth:
            if api_settings.JWT_AUTH_COOKIE:
                return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
            return None

        if smart_text(auth[0].lower()) != auth_header_prefix:
            return None

        if len(auth) == 1:
            msg = _('Invalid Authorization header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid Authorization header. Credentials string '
                    'should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        return auth[1]


    def authenticate(self, request):
        """
        Returns a two-tuple of `User` and token if a valid signature has been
        supplied using JWT-based authentication.  Otherwise returns `None`.
        """
        jwt_value = self.get_jwt_value(request)
        if jwt_value is None:
            return None

        try:
            payload = jwt_decode_handler(jwt_value)
        except jwt.ExpiredSignature:
            raise exceptions.AuthenticationFailed(f'Token {jwt_value}已过期')
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed('Token非法')

        user = self.authenticate_credentials(payload)

        return (user, jwt_value)

views.py

默认使用JSONWebTokenAuthentication这个类 进行认证, 现在使用自定义的认证类jwtAuthentication

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated


class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')


汽车接口测试

新增(post)

Headers


Body



查(get)


过滤:django-filter

安装

pip3 install django-filter

注册apps

settings.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',
    'django_filters'
]

视图层配置过滤

默认是and 查询

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')


#     过滤
    filter_backends = [DjangoFilterBackend]
# 一般过滤字段为分类字段
#     filter_fields = ('brand', 'price')
    filterset_fields = ('brand', 'price')

postman测试



筛选

视图层

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')


#     过滤
#     filter_backends = [DjangoFilterBackend]
# 一般过滤字段为分类字段
#     filter_fields = ('brand', 'price')
#     filterset_fields = ('brand', 'price')

#     筛选
    filter_backends = [DjangoFilterBackend, SearchFilter]
    search_fields = ('name', 'price')

postman测试

只能做单条件匹配


排序


修改代码

修改serializers.py

from rest_framework.serializers import ModelSerializer
from api import models


class CarsModelSerializer(ModelSerializer):
    class Meta:
        model = models.Car
        fields = ('id','name', 'price', 'brand', 'brandName')

        extra_kwargs = {
            'id': {
                'read_only': True
            },
            'brand': {
                'write_only': True
            }
        }

Postman 查看get 请求能获得ID


views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')


#     过滤
#     filter_backends = [DjangoFilterBackend]
# 一般过滤字段为分类字段
#     filter_fields = ('brand', 'price')
#     filterset_fields = ('brand', 'price')

#     筛选
#     filter_backends = [DjangoFilterBackend, SearchFilter]
#     search_fields = ('name', 'price')

#     排序
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ('name', 'price')
    ordering_fields = ('id',)


分页

基础分页

在应用目录新建paginations.py

from rest_framework import pagination

class PageNumberPagination(pagination.PageNumberPagination):
    # 一页的条数
    page_size = 3
    # 接口最终选页码的字段名 - 一般不修改
    page_query_param = 'page'

    # 用户可以通过接口自定义一页条数  ?page=1&page_size=一页的条数
    page_size_query_param = 'page_size'
    # 用户可以自定义的最大一页条数, 超过就采用最大值
    max_page_size = 4

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from api import paginations

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')


#     过滤
#     filter_backends = [DjangoFilterBackend]
# 一般过滤字段为分类字段
#     filter_fields = ('brand', 'price')
#     filterset_fields = ('brand', 'price')

#     筛选
#     filter_backends = [DjangoFilterBackend, SearchFilter]
#     search_fields = ('name', 'price')

#     排序
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ('name', 'price')
    ordering_fields = ('id', 'price')

#     分页
    pagination_class = paginations.PageNumberPagination



偏移分页

paginations.py

from rest_framework import pagination

# 偏移分页  接口:?offset=0&limit=3  从第一条往后查3条  limit  控制显示条数 offset 负责偏移
class LimitOffsetPagination(pagination.LimitOffsetPagination):
    # 一页的条数
    default_limit = 2
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    # 用户可以自定义最大的一项条数, 超过就采用最大值
    max_limit = 4

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from api import paginations

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')


#     过滤
#     filter_backends = [DjangoFilterBackend]
# 一般过滤字段为分类字段
#     filter_fields = ('brand', 'price')
#     filterset_fields = ('brand', 'price')

#     筛选
#     filter_backends = [DjangoFilterBackend, SearchFilter]
#     search_fields = ('name', 'price')

#     排序
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ('name', 'price')
    ordering_fields = ('id', 'price')

#     基础分页: 配置一个类, 不是类们
#     pagination_class = paginations.PageNumberPagination

#     偏移分页
    pagination_class = paginations.LimitOffsetPagination



加密分页

paginations.py

from rest_framework import pagination


# 加密分页
class CursorPagination(pagination.CursorPagination):
    # 一页的条数
    page_size = 2
    # 请求页码数据的字段 - 字段后的参数是加密的
    cursor_query_param = 'cursor'
    # 用户自定义一页的字段和最大值
    page_size_query_param = 'page_size'
    max_page_size = 4
    # 加密分页必须指定该字段 - 不能与drf的ordering组件同时使用
    ordering = 'id'

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from api import paginations

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        return Response('该功能暂无提供')


#     过滤
#     filter_backends = [DjangoFilterBackend]
# 一般过滤字段为分类字段
#     filter_fields = ('brand', 'price')
#     filterset_fields = ('brand', 'price')

#     筛选
#     filter_backends = [DjangoFilterBackend, SearchFilter]
#     search_fields = ('name', 'price')

#     排序
#     filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ('name', 'price')
    ordering_fields = ('id', 'price')

#     基础分页: 配置一个类, 不是类们
#     pagination_class = paginations.PageNumberPagination

#     偏移分页
#     pagination_class = paginations.LimitOffsetPagination

#     加密分页
    pagination_class = paginations.CursorPagination


异常处理模块

源码:/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/rest_framework/views.py


自定义异常处理

从源码中获得句柄名字,源码路径 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/rest_framework/settings.py


settings.py中添加句柄

REST_FRAMEWORK = {
  # 异常句柄
    'EXCEPTION_HANDLER': 'api.exceptions.exception_handler'
}

在应用目录下新建 exceptions.py

from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.status import HTTP_500_INTERNAL_SERVER_ERROR


def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    # drf没有提供处理服务器的异常
    if not response:
        return Response({'status': 500, 'msg': '服务器异常'}, headers=HTTP_500_INTERNAL_SERVER_ERROR)

    response.data = {
        'status': 444,
        'msg': response.data['detail']
    }

    return response


views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from api import paginations

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        # return Response('该功能暂无提供')
        raise Exception(123)
        
#     过滤
#     filter_backends = [DjangoFilterBackend]
# 一般过滤字段为分类字段
#     filter_fields = ('brand', 'price')
#     filterset_fields = ('brand', 'price')

#     筛选
#     filter_backends = [DjangoFilterBackend, SearchFilter]
#     search_fields = ('name', 'price')

#     排序
#     filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ('name', 'price')
    ordering_fields = ('id', 'price')

#     基础分页: 配置一个类, 不是类们
#     pagination_class = paginations.PageNumberPagination

#     偏移分页
#     pagination_class = paginations.LimitOffsetPagination

#     加密分页
    pagination_class = paginations.CursorPagination

postman测试


未处理的异常

exceptions.py

from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.status import HTTP_500_INTERNAL_SERVER_ERROR


def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)
    print(response)
    # drf没有提供处理服务器的异常
    # if response is None:
    #     return Response({'status':500, 'msg': '服务器异常'}, status=HTTP_500_INTERNAL_SERVER_ERROR)

    response.data = {
        'status': 444,
        'msg': response.data['detail']
    }

    return response


status=HTTP_500_INTERNAL_SERVER_ERROR 只能够处理raise 的异常

如果把destroy方法下改成pass,无法处理该异常

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from api.authentications import jwtAuthentication
from api import models, serializers
from rest_framework.response import Response
from api import permissions
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from api import paginations

class CarModelViewSet(ModelViewSet):
    # 局部禁用
    # permission_classes = (permissions.isSuperUserBasePermission, )
    # 局部解禁
    # permission_classes = ()
    # 必须完成jwt校验才能得到登录状态
    # authentication_classes = [JSONWebTokenAuthentication]
    authentication_classes = [jwtAuthentication]

    # 登录后才能查看
    permission_classes = [IsAuthenticated, ]

    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializers.CarsModelSerializer

    def destroy(self, request, *args, **kwargs):
        # return Response('该功能暂无提供')
        pass

#     过滤
#     filter_backends = [DjangoFilterBackend]
# 一般过滤字段为分类字段
#     filter_fields = ('brand', 'price')
#     filterset_fields = ('brand', 'price')

#     筛选
#     filter_backends = [DjangoFilterBackend, SearchFilter]
#     search_fields = ('name', 'price')

#     排序
#     filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    search_fields = ('name', 'price')
    ordering_fields = ('id', 'price')

#     基础分页: 配置一个类, 不是类们
#     pagination_class = paginations.PageNumberPagination

#     偏移分页
#     pagination_class = paginations.LimitOffsetPagination

#     加密分页
    pagination_class = paginations.CursorPagination


posted @ 2021-02-18 00:06  cjw1219  阅读(113)  评论(0编辑  收藏  举报