Django-REST_FRAMEWORK

本文详细介绍了Django REST FRAMEWORK(DRF)的使用,涵盖了请求与响应、路由、视图、序列化、认证、权限、频率限制、过滤器、排序、分页、异常处理、JWT以及RBAC等关键概念。讲解了如何定义和使用Request、Response,自定义路由、视图类,ModelSerializer,以及JWT的内置验证和自定义认证。同时,还涉及到了权限分配和角色控制(RBAC)的应用。

目录

  1. DRF规范
  2. 请求、响应
  3. 路由
  4. 视图
  5. 序列化
  6. 认证、权限、频率
  7. 过滤器、排序、分页、异常处理
  8. JWT
  9. RBAC

DRF规范

REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。
十条RESTful API设计指南
1、数据的安全保障
	url一般采用https协议进行传输,可以提高数据交互过程中安全性
2、接口特征表现
	使用api关键字标识接口url,代表该url是完成前后台数据交互的
3、多数据版本共存
	当一种数据资源有多个版本,应在url中标识数据版本
4、数据即资源
	均使用名词复数表示数据资源
5、请求方式决定资源的操作方式
	GET :从服务器取出全部资源
	GET:后面加数字,从服务器取出单个资源
	POST:在服务器新建一个资源
	PUT:在服务器更新资源(客户端提供改变后的完整资源)
	PATCH:在服务器更新资源(客户端提供改变的属性)
	DELETE :从服务器删除资源
6、响应状态码
	200:常规请求
	201:创建成功
	300:永久重定向
	301:暂时重定向
	403:请求无权限
	404:请求路径不存在
	405:请求方法不存在
	500:服务器异常
7、响应结果
	针对不同操作,服务器向用户返回不同的结果
	GET:返回资源对象的列表(数组)
	GET:后面加数字,返回单个资源对象
	POST:返回新生成的资源对象
	PUT:返回完整的资源对象
	PATCH:返回完整的资源对象
	DELETE:返回一个空文档
8、过滤
	通过url上传参的形式传递搜索条件
	https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
	https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
	https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
	https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
	https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
9、错误处理
	请求出错时,应返会错误信息
10、Hypermedia API
	返回结果中提供链接

请求、响应

请求

Request

基于Django的request进行重新封装,Django的request的属性都可以使用。
对于POST请求、PUT请求的数据通过request.data获取
对于GET请求的参数通过request.query_params获取

解析器

根据请求头content-type选择对应的解析器对请求体内容进行处理

JSONParser:仅处理content-type为application/json的请求头
FormParser:仅处理请求头content-type为application/x-www-form-urlencoded 的请求体
MultiPartParser:仅处理请求头content-type为multipart/form-data的请求体
FileUploadParser:仅处理上传的文件
局部使用
重写类的parser_classes属性
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser, MultiPartParser, FileUploadParser, FormParser

class BookView(APIView):
    parser_classes = [JSONParser, MultiPartParser]			# 局部使用,重写类的parser_classes属性
    
    def get(self, request):
        pass
    
    def post(self, request):
        pass
全局使用
配置settings.py
REST_FRAMEWORK = {
	'DEFAULT_PARSER_CLASSES':[
		'rest_framework.parsers.JSONParser',
		'rest_framework.parsers.FormParser',
		'rest_framework.parsers.MultiPartParser'
	]
}

响应

Response
自己封装Response
1.继承Response
2.重写__init__方法
3.调用父类的__init__方法

from rest_framework.response import Response

class ApiResponse(Response):
    def __init__(self, code=100, msg='', err='', data=None, status=None, headers=None, **kwargs):
        dic = {'code': code}
        if msg:
            dic['msg'] = msg
        if err:
            dic['err'] = err
        if data:
            dic['data'] = data
        dic.update(kwargs)
        super().__init__(data=dic, status=status, headers=headers)
渲染器
内置渲染器
默认显示格式:BrowsableAPIRenderer
	访问URL方式:
		http://127.0.0.1:8000/test/?format=api
		http://127.0.0.1:8000/test.api
		http://127.0.0.1:8000/test/
		
显示json格式:JSONRenderer
	访问URL方式:
		http://127.0.0.1:8000/test/?format=json
		http://127.0.0.1:8000/test.json
		http://127.0.0.1:8000/test/
		
显示表格格式:AdminRenderer
	访问URL方式:
		http://127.0.0.1:8000/test/?format=admin
		http://127.0.0.1:8000/test.admin
		http://127.0.0.1:8000/test/
		
显示表单格式:HTMLFormRenderer
	访问URL方式:
		http://127.0.0.1:8000/test/?format=form
		http://127.0.0.1:8000/test.form
		http://127.0.0.1:8000/test/

自定义显示模板格式:TemplateHTMLRenderer
局部使用
重写视图类的renderer_classes属性
from rest_framework.views import APIView
from rest_framework.renderers import HTMLFormRenderer, BrowsableAPIRenderer

class BookView(APIView):
    renderer_classes = [HTMLFormRenderer, BrowsableAPIRenderer]  # 局部使用,重写类的renderer_classes属性

    def get(self, request):
        pass

    def post(self, request):
        pass	
全局使用
配置settings.py
REST_FRAMEWORK = {
	'DEFAULT_RENDERER_CLASSES':['rest_framework.renderers.JSONRenderer',
								'rest_framework.renderers.BrowsableAPIRenderer']
}

路由

自定义路由(原始方法)

from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('test/', views.TestView.as_view()),
    re_path(r'^test/(?P<pk>\d+)$', views.TestView.as_view())
]

半自动路由(视图类继承ViewSetMixin)

传入一个字典{'请求方式''视图对应的函数名'}
from django.urls import path, re_path
from app01 import views


urlpatterns = [
    path('test/', views.TestView.as_view({'get': 'list', 'post': 'create'})),		# 传入一个字典{'请求方式':'视图对应的函数名'}
    re_path(r'test/(?P<pk>\d+)$', views.TestView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
]

全自动路由(自动生成)

1.导入自动生成路由的类
2.实例化
3.注册视图函数
4.拼接路由

# urls
from rest_framework.routers import SimpleRouter, DefaultRouter
from app01 import views

route1 = SimpleRouter()										# 实例化
route2 = DefaultRouter()
route1.register('app01/test1', views.TestView)				# 注册视图函数,第一个参数为路由地址,第二给参数为视图类
route2.register('app01/test2', views.Test2View)

urlpatterns += route1.urls									# 拼接路由,urls为自动生成的路由列表
urlpatterns += route2.urls

action装饰器:函数使用action装饰器装饰后,会自动生成路由
# views.py
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from app01 import models
from utils import ser


class TestView(ModelViewSet):
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = ser.TestSer

    @action(methods=['get'], detail=True, url_path='ccc')
   	"""
   	methods:请求方式列表
    detail:决定是否带有pk值
    	当为True时生成的路由:^app01/test1/(?P<pk>[^/.]+)/ccc/$ [name='agvtestrecord-action']
    	当为False时生成的路由:^app01/test1/ccc/$ [name='agvtestrecord-action']
    url_path:生成的url路径名,默认为被装饰函数名
    url_name:反向解析名,默认为被装饰函数名
    """
 
    def action(self, request, *args, **kwargs):
        return Response('OK')

视图

两个视图基类

ApiView:基类,继承View
GenericView:基类,继承ApiView
ApiView
from rest_framework.views import ApiView
from utils.utils import ApiResponse
from app01 import models
from utils import sers

常用类属性:
	parser_classes:元组或列表,配置解析器类
	renderer_classes:元组或列表,配置渲染器类
	authentication_classes:元组或列表,配置认证类
	permission_classes:元组或列表,配置权限类
	throttle_classes:元组或列表,配置频率类

class TestView(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        if pk:
            obj = models.AgvTestRecord.objects.filter(pk=pk).first()
            ser_obj = sers.TestSer(obj)
            return ApiResponse(code=100, msg='success', data=ser_obj.data)
        else:
            obj = models.AgvTestRecord.objects.all()
            ser_obj = sers.TestSer(obj, many=True)
            return ApiResponse(code=100, msg='success', data=ser_obj.data)
            
	def post(self, request, *args, **kwargs):
        obj = sers.TestSer(data=request.data)
        obj.is_valid(raise_exception=True)
        obj.save()
        return ApiResponse(code=101, msg='success', data=obj.data)
GenericApiView
from rest_framework.generics import GenericAPIView
from utils.utils import ApiResponse
from app01 import models
from utils import sers

常用类属性:
	queryset:模型类对象的全部
    serializer_class:序列化类

常用方法:
	self.get_queryset():获得全部的queryset对象
	self.get_object():获得单个queryset对象
	self.get_serializer(*args, **kwagrs):获得序列化类,并且序列化及反序列化

class TestView(GenericAPIView):
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer

    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        if pk:
            obj = self.get_object()
            ser_obj = self.get_serializer(obj)
            return ApiResponse(code=100, msg='success', data=ser_obj.data)
        else:
            obj = self.get_queryset()
            ser_obj = self.get_serializer(obj, many=True)
            return ApiResponse(code=100, msg='success', data=ser_obj.data)

    def post(self, request, *args, **kwargs):
        obj = self.get_serializer(data=request.data)
        obj.is_valid(raise_exception=True)
        obj.save()
        return ApiResponse(code=101, msg='success', data=obj.data)

五个视图拓展类

一般与GenericApiView一起使用
CreateModelMixin:创建
ListModelMixin:查看所有
RetrieveModelMixin:查看单条
UpdateModelMixin:修改
DestroyModelMixin:删除
ListModelMixin
源码,内部仅有一个list方法用于获取全部内容
class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
CreateModelMixin
源码,内部有一个create方法用于创建
class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}
RetrieveModelMixin
源码,内部仅有一个retrieve方法用于获取单条
class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)
UpdateModelMixin
源码,内部有一个update方法用于修改
class UpdateModelMixin:
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
        if getattr(instance, '_prefetched_objects_cache', None):
            instance._prefetched_objects_cache = {}
        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)
DestroyModelMixin
源码,内部有一个destroy方法用于删除
class DestroyModelMixin:
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()
基本使用
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
from app01 import models
from utils import sers

class TestView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

九个子类视图

CreateAPIView:提供POST方法,继承自GenericApiView和CreateModelMixin
ListAPIView:提供GET方法,继承自GenericApiView和ListModelMixin
RetrieveAPIView:提供GET方法,继承自GenericApiView和RetrieveModelMixin
DestroyAPIView:提供DELETE方法,继承自GenericApiView和DestroyModelMixin
UpdateAPIView:提供PUT方法,继承自GenericApiView和UpdateModelMixin
ListCreateAPIView:提供GET和POST方法,继承自GenericApiView和ListModelMixin和CreateModelMixin
RetrieveUpdateAPIView:提供GET和PUT方法,继承自 GenericAPIview和UpdateModelMixin和RetrieveModelMixin
RetrieveDestroyAPIView:提供GET和DELETE方法,继承自GenericApiView和RetrieveModelMixin和DestroyModelMixin
RetrieveUpdateDestroyAPIView:提供GET和PUT和DELETE方法,继承自GenericApiVIew和RetrieveModelMixin和UpdateModelMixin和DestroyModelMixin
基本使用
from rest_framework.generics import CreateAPIView,ListAPIView
from app01 import models
from utils import sers

class TestView(ListAPIView, CreateAPIView):
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer

视图集

重写了as_view方法增加action参数,根据action参数决定调用方法
ViewSetMixin:视图集基类,重写了as_view()方法
ViewSet:继承ViewSetMixin和ApiView
GenericViewSet:继承ViewSetMixin和GenericApiView
ModelViewSet:继承GenericApiView和五个视图字类
ReadOnlyModelViewSet:继承RetrieveModelMixin和ListModelMixin和GenericViewSet
基本使用
from rest_framework.viewsets import ModelViewSet
from app01 import models
from utils import sers

class TestView(ModelViewSet):
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer

序列化

serializer

定义表模型
from app01 import models

class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)

    class Meta:
        abstract = True									# 设置该表为虚拟表,数据库内不创建

cclass AgvTestRecord(BaseModel):
    recv_date = models.DateField(auto_now_add=True, verbose_name='接受日期')
    type_choices = (("0", '朱雀'), ('1', '玄武'), ('2', '麒麟'), ('3', '其他'))
    agv_type = models.CharField(max_length=32,choices=type_choices, verbose_name='车型')
    agv_ip = models.CharField(max_length=32, verbose_name='ip')
    agv_num = models.CharField(max_length=32, verbose_name='车架号')
    version = models.CharField(max_length=32, verbose_name='版本')
    result = models.BooleanField(verbose_name='测试结果')

    def __str__(self):
        return self.agv_ip

    def agv_type_name(self):
        return self.get_agv_type_display()				# get_field_display()方法获得choices的中文部分

    class Meta:
        db_table = 'test_record'						# 配置表的名族
        verbose_name = '测试记录'						# 别名
        verbose_name_plural = verbose_name				# 复数形式,默认是别名+s


class AgvQuestion(BaseModel):
    happen_date = models.DateField(auto_now_add=True, verbose_name='问题日期')
    description = models.CharField(max_length=64, verbose_name='问题描述')
    reason = models.CharField(max_length=64, verbose_name='问题原因', null=True)
    solution = models.CharField(max_length=64, verbose_name='解决方法', null=True)
    solve_date = models.DateField(verbose_name='解决日期', null=True)
    status = models.BooleanField(verbose_name='状态')
    agv_num = models.ForeignKey('AgvTestRecord', on_delete=models.DO_NOTHING, db_constraint=False)
    """
    配置db_constraint=False实现表断关联
	1、表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
	2、断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(不影响增删改查操作)
	3、断关联一定要通过逻辑保证表之间数据的安全
	4、断关联
	5、级联关系
	    作者没了,详情也没:on_delete=models.CASCADE
	    出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING
	    部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL
	    部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT

    """

    def __str__(self):
        return self.description

    @property
    def agv_ip(self):
        return self.agv_num.agv_ip						# 深度查询

    class Meta:
        db_table = 'test_question'
        verbose_name = '测试问题'
        verbose_name_plural = verbose_name

定义序列化器
from rest_framework import serializers

反序列化
1、创建数据,需要重写父类的create方法,接受的参数为验证过的值
2、修改数据,需要重写父类的update方法,接受的参数为验证过的值

验证
1、局部验证:定义validate_字段名函数,接受该字段的值value,进行验证
2、全局验证:定义validate函数,接受request数据,进行验证
3、验证器:定义验证函数,通过参数validators配置给该字段,可配置多个

验证器
def func1():
	pass
	
def func2():
	pass

class TestSer(serializers.Serializer):
	result = serializers.CharField(source='result', validators=[func1, func2])			# 使用验证器,source参数即为AgvTestRecord.result
	questions = serializers.SerializerMethodField(read_only=True)						# 必须配套get_字段名方法
	agv_type = serializers.CharField()
	agv_ip = serializers.CharField()
	agv_num = serializers.CharField()
	version = serializers.CharField()
	
   def get_questions(self, obj):														# 配套SerializerMethodField字段使用
       temp = []
       for question in obj.agvquestion_set.all():
           temp.append(question.description)
       return temp
        
	# 局部验证
	def validate_agv_num(self, value):							# value为校验字段的值
		if '666' not in value:
			raise serializers.ValidationError('不能没有666!')
		else:
			return value
			
	# 全局验证
	def validate(self, data):									# data为校验的全部数据
		agv_ip= data.get('agv_ip')
		agv_num= data.get('agv_num')
		if agv_ip== agv_num:
			raise serializers.ValidationError('不合理!')
		else:
			return data

	# 反序列化创建数据
	def create(self, validated_data):
		return AgvTestRecord.objects.create(**validated_data)
	
	# 反序列化修改数据
	def update(self, instance, validated_data):
		instance.agv_type= validated_data.get('agv_type')
		instance.agv_ip= validated_data.get('agv_ip')
		instance.agv_num= validated_data.get('agv_num')
		instance.version= validated_data.get('version')
		instance.result= validated_data.get('result')
		instance.save()
		return instance

常用字段:
	BooleanField():布尔字段
	CharField():字符字段
	EmailField():邮箱字段
	IntegerField():整数字段
	DecimalField():小数字段
	DateField():日期字段
	
常用参数:
	source:如果是字段就显示字段,如果是方法就会执行方法
	max_length:最大长度
	min_length:最小长度
	allow_blank:是否允许为空
	trim_whitespace:是否截断空白字符
	max_value:最大值
	min_value:最小值
	read_only:表明该字段仅用于序列化输出,默认为False
	write_only:表面该字段仅用于反序列化输入,默认为False
	required:表面该字段在反序列化时必须输入,默认为True
	default:反序列化时使用的默认值
	allow_null:表面该字段是否允许传入None,默认为False
	label:显示字段的名称
	error_messages:错误信息的字典
	validators:验证器

视图函数

feom rest_framework.views import APIView
from rest_framework.response import Response
form app01 import models
from app01.ser import BookSerializer

创建Serializer对象
Serializer(instance=None, data=empty, **kwargs)
用于序列化时,将模型类对象传给instance参数
用于反序列化时,将要被反序列化的数据传给data参数

class Test1View(APIView)
	# 序列化获得单个值
	def get(self, request, pk, *args, **kwargs):
		obj = models.AgvTestRecord.objects.filter(pk=pk)
		ser_obj = TestSer(obj)
		return Response(ser_obj.data)
	
	# 反序列化修改单个值
	def put(self, request, pk, *args, **kwargs):
		res = {"code": 0}
		obj = models.AgvTestRecord.objects.filter(pk=pk)
		ser_obj = TestSer(instance=book, data=request.data, partial=True)  	# 设置允许部分字段更新
		if ser_obj.is_valid():								# 校验数据,通过返回True,反之返回False
			ser_obj.save()									# 修改保存,必须重写了父类的update方法
			return Response(res)
		else:
			res["code"] = 1
			res["err"] = ser_obj.errors						# 报错信息
			return Response(res)

class Test2View(APIView):	
	# 序列化获得多个值
	def get(self, request, *args, **kwargs):
		obj_list = models.AgvTestRecord.objects.all()
		ser_obj = TestSer(book_list, many=True)		# 序列化多个信息,需要设置many=True
		return Response(ser_obj.data)
	
	# 反序列化增加单个值
	def post(self, request, *args, **kwargs):
		res = {"code": 0}
		ser_obj = TestSer(data=request.data)
		if ser_obj.is_valid():								# 校验数据,通过返回True,反之返回False
			ser_obj.save()									# 创建保存,必须重写了父类的create方法
			return Response(res)
		else:
			res["code"] = 1
			res["err"] = ser_obj.errors
			return Response(res)	
ps:
调用ser_obj.save()保存时,可以传递参数,这些数据可以在create()和update()中的validated_data参数获取到

ModelSerializer

from rest_framework import serializers
from app01 import models

class TestSer(serializers.ModelSerializer):
    class Meta:
        model = models.AgvTestRecord
        fields = ('id', 'recv_date', 'agv_type', 'agv_type_name', 'agv_ip', 'agv_num', 'version', 'result', 'is_delete')
        extra_kwargs = {
            'agv_type': {'write_only': True},
            'is_delete': {'write_only': True},
            'agv_type_name': {'read_only': True},
            'id': {'read_only': True}
        }

参数:
	model:指明的模型类
	fields:指明生成模型类的那些字段,__all__表示全部字段,指明某些字段使用元组包起
	exclude:指明排除那些字段,用法与fields相同,两者只能存在一个
	extra_kwargs:添加额外参数,字典形式

认证、权限、频率

认证组件

自定义认证器
1.继承BaseAuthentication
2.重写authenticate方法
3.返回值:认证通过返回两个值,用户和任意参数,认证失败直接抛出异常

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

class ApiAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        if token:
            obj = models.Token.objects.filter(token_data=token).first()
            if obj:
                now_time = datetime.datetime.now()
                expire_date = obj.expire_date.replace(tzinfo=None)
                if expire_date > now_time:
                    return obj, token
                else:
                    raise AuthenticationFailed('token过期!')
            else:
                raise AuthenticationFailed('认证失败!')
        else:
            raise AuthenticationFailed('无token参数!')
局部使用
局部使用,只需要在视图类里加入:authentication_classes = [ApiAuthentication,]

from api import models
from utils import sers
from utils.utils import TestViewSet, ApiAuthentication

class TestView(TestViewSet):
    authentication_classes = [ApiAuthentication,]
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["utils.utils.ApiAuthentication",]
}

权限组件

自定义权限器
1.继承BasePermission
2.重写has_permission方法
3.返回值:有权限返回True,无权限返回False

from rest_framework.permissions import BasePermission

class ApiPermission(BasePermission):
	message = "必须是管理员才能访问"  				# 这里的message表示如果不通过权限的时候,错误提示信息
    def has_permission(self, request, view):
        permission = request.user.user.permission
        if permission == '1':
            return False
        else:
            return True
局部使用
局部使用,只需要在视图类里加入:permission_classes = [ApiPermission,]

from api import models
from utils import sers
from utils.utils import TestViewSet, ApiPermission

class TestView(TestViewSet):
    permission_classes = [ApiPermission,]
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置

REST_FRAMEWORK={
    "DEFAULT_PERMISSION_CLASSES":["utils.utils.ApiPermission",]
}

频率组件

内置频率器
AnonRateThrottle:未登录用户ip限制,需要配合auth模块用
UserRateThrottle:登录用户频率限制,这个得配合auth模块来用
自定义频率器
1.继承SimpleRateThrottle
2.重写get_cache_key方法
3.重写scope类属性,scope与settings.py的DEFAULT_THROTTLE_RATES配合使用
3.返回值:返回的数据为限制条件,返回None及不进行限制

from rest_framework.throttling import SimpleRateThrottle

class ApiThrottle(SimpleRateThrottle):
    scope = 'api'

    def get_cache_key(self, request, view):
        return request.META.get("REMOTE_ADDR")
局部使用
局部使用,只需要在视图类里加入:throttle_classes = [ApiThrottle,]

from api import models
from utils import sers
from utils.utils import TestViewSet, ApiThrottle

class TestView(TestViewSet):
    throttle_classes = [ApiThrottle]
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置

REST_FRAMEWORK={
	'DEFAULT_THROTTLE_CLASSES':['utils.utils.ApiThrottle'],
	'DEFAULT_THROTTLE_RATES':{									# 配置限制次数,局部和全局
        'api':'3/m'												# key与自定义的频率类的scope属性相同
    }
}

过滤器、排序、分页、异常处理

过滤器

安装

pip install django-filter

使用
1.在settings里配置过滤器
2.在views里配置过滤字段
3.访问url时添加,过滤字段=过滤条件

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

# views.py
from api import models
from utils import sers

class TestView(TestViewSet):
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer
    filter_fields = ('agv_ip', 'agv_type')				# 重写filter_fields属性配置过滤字段

排序器

使用
1.在views里配置排序器
2.在views里配置可排序字段
3.访问url时添加,ordering=排序字段

# views.py
from rest_framework.filters import OrderingFilter			# 导入排序器
from api import models
from utils import sers

class TestView(TestViewSet):
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer
    filter_backends = [OrderingFilter]						# 配置排序器
    ordering_fields = ('id', 'agv_num')						# 配置排序字段

分页器

内置分页器

PageNumberPagination:简单分页,查看第n页,每页显示n条数据
LimitOffsetPagination:偏移分页,在n个位置,向后查看n条数据
CursorPagination:加密分页,只能查看上下页,速度最快

自定义分页器
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

class ApiPageNumberPagination(PageNumberPagination):
    """
    page_size:每页显示数目
    page_query_param:前端发送的页数关键字名,默认为"page"
    page_size_query_param:前端发送的每页数目关键字名,默认为None
    max_page_size前端最多能设置的每页数量
    """
    page_size = 5
    max_page_size = 10
    page_size_query_param = 'size'


class ApiLimitOffsetPagination(LimitOffsetPagination):
    """
    default_limit:默认偏移大小
    limit_query_param:偏移量名,偏移的大小,默认为'limit'
    offset_query_param:偏移标杆名,开始偏移位置,默认为'offset'
    max_limit:最大偏移限制,默认None
    """
    default_limit = 5
    max_limit = 10


class ApiCursorPagination(CursorPagination):
    """
    page_size:每页显示数目
    cursor_query_param:前端发送的页数关键字名,默认为"cursor"
    ordering:排序方式
    """
    page_size = 5
    ordering = 'id'	
局部使用
局部使用,只需要在视图类里加入:pagination_class = ApiCursorPagination

from utils.utils import ApiPageNumberPagination, ApiLimitOffsetPagination, ApiCursorPagination
from api import models
from utils import sers

class TestView(TestViewSet):
    pagination_class = ApiPageNumberPagination
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':  'utils.utils.ApiPageNumberPagination',
}
其他使用
1.获得全部数据
2.实例化分页器
3.在数据库获得分页数据,调用paginate_queryset方法
4.对获得分页数据序列化
5.返回序列化的数据,调用get_paginated_response方法

from rest_framework.views import APIView
from api import models
from utils import sers
from utils.utils import ApiPageNumberPagination

class TestView(APIView):
    def get(self,request):
        obj_list = models.AgvTestRecord.objects.all()						# 获得全部数据
        page = ApiPageNumberPagination()									# 实例化分页器
        page_list = page.paginate_queryset(obj_list, request, view=self)	# 在数据库获得分页数据
        ser = sers.TestSer(page_list, many=True)							# 序列化
        return page.get_paginated_response(ser.data)						# 返回序列化的数据

异常处理

异常处理器
重写exception_handler函数
1.调用原方法,得到一个结果
2.根据此结果进行二次处理
参数:
	exc:报错信息
	context:包含了任何额外上下文信息(例如当前被处理的视图)的字典

from rest_framework.views import exception_handler

def api_exception_handler(exc, context):
    response = exception_handler(exc, context)			# 调用原方法,继承器原来的处理方式,根绝结果进行二次处理		
    if response is None:
        return ApiResponse(code=701, err=str(exc), status=status.HTTP_400_BAD_REQUEST)
    else:
        err = response.data.get('detail', None)			# 返回的response对象的data属性即报错信息
        if err is None:
            err = response.data
        return ApiResponse(code=response.status_code, err=err, status=status.HTTP_400_BAD_REQUEST)
使用
在settings里配置

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'utils.utils.api_exception_handler'
}

JWT

工作原理:由头部(header).载荷(payload).签证(signature)组成
header:声明类型和加密算法,json数据,使用base64加密,可反解
payload:存放有效信息,一般包含标准注册声明,公有声明,私有声明,json数据,使用base64加密,可反解
signature:由加密后的header.加密后的payload.密钥组成,使用hash256加密,不可反解
密钥:固定字符串,存放在服务器
安装:pip install djangorestframework-jwt

内置验证

内置视图类

from rest_framework_jwt.views import ObtainJSONWebToken, VerifyJSONWebToken, RefreshJSONWebToken
ObtainJSONWebToken
VerifyJSONWebToken
RefreshJSONWebToken

内置认证类

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
JSONWebTokenAuthentication

自定义认证类

自定义jwt视图返回结果
	重写jwt_response_payload_handler方法
	
自定义jwt认证
	1.继承BaseJSONWebTokenAuthentication类
	2.重写get_jwt_value方法
	3.返回值:获得jwt字符串,有则返回,无则随便返回个字符串

from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework.authentication import get_authorization_header
from utils import sers

def jwt_response_payload_handler(token, user=None, request=None):
	"""return 想要返回的格式"""
    return {
        'status': 100,
        'msg': 'ok',
        'data': {
            'token': token,
            'user': sers.UserSer(user).data.get('username')
        }
    }

class JwtAuthentication(BaseJSONWebTokenAuthentication):

    def get_jwt_value(self, request):
        auth = get_authorization_header(request)		# 调用get_authorization_header方法获得jwt字符串
        if not auth:
            return 'none'								# 没有jwt字符串,随便返回个字符串,即验证失败
        return auth										# 有jwt字符串,返回字符串

使用

局部使用
局部使用,只需要在视图类里加入:authentication_classes = [utils.JwtAuthentication]
from app01 import models
from utils import sers
from utils import utils

class TestView(utils.TestViewSet):
    authentication_classes = [utils.JwtAuthentication]
    queryset = models.AgvTestRecord.objects.all()
    serializer_class = sers.TestSer
全局使用
全局使用,在settings里配置

import datetime

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ('utils.utils.JwtAuthentication',)		# 自定义jwt认证类
}

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),		# 过期时间
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'utils.utils.jwt_response_payload_handler',					# 自定义视图返回结果
}

多方式登录

1.自定义序列化类
2.自定义视图类

# sers.py
import re
from rest_framework_jwt.settings import api_settings
from app01 import models

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER			# 手动签发的方法
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER			# 手动签发的方法

class UserSer(serializers.ModelSerializer):
    username = serializers.CharField()							# user表内username是唯一字段,需要覆盖username,才能进行多方式登录

    class Meta:
        model = models.User
        fields = ['username', 'password']

    def validate(self, attrs):									# 全局校验,验证用户和密码是否正确
        username = attrs.get('username')
        password = attrs.get('password')
        re_phone = r'^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$'
        re_email = r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$'
        if re.match(re_phone, username):
            user = models.User.objects.filter(phone=username).first()
        elif re.match(re_email, username):
            user = models.User.objects.filter(email=username).first()
        else:
            user = models.User.objects.filter(username=username).first()
        if user:
            if user.check_password(password):
                payload = jwt_payload_handler(user)				# 手动签发
                token = jwt_encode_handler(payload)				# 手动签发
                self.context['token'] = token					# 将token放入公用字典内,方便在视图里取出
                self.context['username'] = user.username
                return attrs
            else:
                raise serializers.ValidationError('密码错误!')
        else:
            raise serializers.ValidationError('用户名不存在!')

# views.py
from rest_framework.views import APIView
from utils import sers
from utils import utils

class Login(APIView):
    def post(self, request, *args, **kwargs):
        ser_obj = sers.UserSer(data=request.data)
        ser_obj.is_valid(raise_exception=True)
        token = ser_obj.context.get('token')					# 将token从公用字典内取出
        username = ser_obj.context.get('username')
        return utils.ApiResponse(code=100, msg='ok', username=username, token=token)

RBAC

RBAC:基于角色的访问控制,在RBAC中,权限与角色相关联,用户通过成为适当的角色而得到该角色的权限,这极大的简化了权限管理。这种管理是层级相互依赖的,权限赋予角色,角色赋予用户。

权限六表:用户表、分组表、权限表、用户-分组表、用户-权限表、分组-权限表

使用

from django.contrib import admin
from app01 import models
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin

# 自定义User表后,admin界面管理User类
class UserAdmin(DjangoUserAdmin):							
    # 添加用户操作字段
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('username','password', 'is_staff', 'phone', 'groups', 'user_permissions'),
        }),
    )
    # 展示用户呈现的字段
    list_display = ('username', 'phone', 'is_staff', 'is_active', 'is_superuser')

# 使用admin注册模型表
admin.site.register(models.User, UserAdmin)
admin.site.register(models.AgvTestRecord)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值