内容概要

  • 反序列化类校验部分源码解析

  • 断言

  • drf-请求

  • drf-响应

  • 视图组件介绍及两个视图基类

  • 基于GenericAPIView+5个视图扩展类

反序列化类校验部分源码分析

反序列化类校验时机

# 反序列校验顺序
	视图类中导入的序列化类对象 通过句点符点is_valid()方法后 开始进行校验
    # serializer_obj = XXSerializer() 在视图类中产生序列化对象

所以我们应从serializer_obj.is_valid()方法中看看经过了什么步骤

我们先假设序列化类为
	BookSerializer  # 继承的是Serializer
    我们去类中寻找is_vialid()方法 
    但是这个类中并没有该方法,我们就应该去BookSerializer继承的类中去找
    所以到了Serializer类中寻找is_vialid()方法
    但是Serializer类中也没有,那就继续在Serializer继承的类中去找
    最终:我们可以在BaseSerializer中找到is_viald()方法

image-20230203145100388

    def is_valid(self, *, raise_exception=False):
        assert hasattr(self, 'initial_data'), (
            'Cannot call `.is_valid()` as no `data=` keyword argument was '
            'passed when instantiating the serializer instance.'
        )
		# 判断self 有没有_vaildated_data属性,如果没有就赋值给它,现在self就是我们 !序列化产生的对象!因为我们是serializer_obj.is_viald()进入的
        # 既然如此我们就需要看run_validation(self.initial_data)赋值了什么东西
        #self序列化类的对象,属性中没有_validated_data,一定会走这句【核心】
     
        if not hasattr(self, '_validated_data'):
            try:
                self._validated_data = self.run_validation(self.initial_data)
            except ValidationError as exc:
                self._validated_data = {}
                self._errors = exc.detail
            else:
                self._errors = {}

        if self._errors and raise_exception:
            raise ValidationError(self.errors)

        return not bool(self._errors)

我们需要进入run_validation(self.initial_data)中看一下,但是self是序列化对象,所以我们需要从头找起,而不是直接使用ctrl点进去,如果点进去我们找到的方法就是BaseSerializer继承的FIled字段中的run_validation方法

image-20230203145700937

我们在Serializer类中找到了run_validation(self.initial_data)

    def run_validation(self, data=empty):
        # 字段自己的,validates方法
        (is_empty_value, data) = self.validate_empty_values(data)
        if is_empty_value:
            return data
        # 局部钩子----【局部钩子】
        value = self.to_internal_value(data)
        try:
            self.run_validators(value)
            # 全局钩子--》如果在BookSerializer中写了validate,优先走它,非常简单
            value = self.validate(value)
            assert value is not None, '.validate() should return the validated data'
        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))

        return value

全局钩子

    def validate(self, attrs):
        return attrs
	全局钩子很简单,就是我们正常编写的validate 全局钩子,我们使用全局钩子直接在序列化中重写方法即可
	但是需要注意的是 必须要返回 attrs  !

局部钩子

# 局部钩子  self.to_internal_value(data)    ---》self是BookSerializer的对象,从根上找
     def to_internal_value(self, data):
        ret = OrderedDict()
        errors = OrderedDict()
        fields = self._writable_fields
        # fields写在序列化类中一个个字段类的对象
        for field in fields:
            # self BookSerializer的对象,反射validate_name
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            try:
                # 在执行BookSerializer类中的validate_name方法,传入了要校验的数据
               validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail
          
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret

断言

# 源码中大量使用try和断言

#  关键字assert ,有什么作用,断定你是xx,如果不是就抛异常


name = 'xiaoiming'

 if name == 'xiaoiming':
     print('对了')
 else:
     print('错了')
     raise Exception('名字不为xiaoiming,不能继续走了')


assert name=='xiaoiming'   # 断定是,如果不是,就抛异常

print('后续代码')

drf-请求

Request能够解析的前端传入的编码格式

# 需求: 接口只能够接收json格式数据,不能接收其他格式
方式1: 
    我们可以在继承自APIView及其子类的视图类中进行配置(局部配置)
    # 总共有三个编码格式:
    from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
class BookView(APIView):
    parser_classes = [JSONParser,]  # APIView中的类属性
方式2:
    在配置文件中配置(影响所有接口!全局生效)
    django中有默认配置,每个项目都有配置
	drf也有默认配置,每个项目也有相应配置
    	就在django的配置文件中
        REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        # 'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        # 'rest_framework.parsers.MultiPartParser',
    ],
}
        # 注释掉什么 就不可以通过 接收该编码格式
        
# 方式三:全局配了1个,某个视图类想要3个,怎么配?
	-只需要在视图类,配置3个即可
    -因为:先从视图类自身找,找不到,去项目的drf配置中找,再找不到,去drf默认的配置找

Request类中属性/方法

视图类方法中的request 
class BookView(APIView) 
	# 继承了APIView 的视图类中request和 继承View 的视图类中 reuqest 不一样!
   	新 request 中
    data         ~ 数据为字典格式
    __getattr__
    query_params ~ request.GET

Resposne能够响应的编码格式

drf 是django的一个 app 所以我们需要进行注册
	之前 drf没有报错是因为我们没有使用到 相应的功能
	如果我们通过postman访问接口,和浏览器直接访问接口
    数据会不一样
    也就是说drf自动做了判断,如果不是浏览器访问接口就返回数据
    浏览器访问接口的话就会返回相应格式
需求:
    如果是浏览器访问接口,数据展示好看一些,如果是postman访问接口只要json数据

# 方式一:
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
    class BookView(APIView):
    	renderer_classes=[JSONRenderer,]	
# 方式二:在项目配置文件中写(全局配置)
    REST_FRAMEWORK = {
      'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
}
   
# 方式三:使用顺序(一般就用内置的即可)
	优先使用视图类中的配置,其次使用项目配置文件中的配置,最后使用内置的

Resposne的源码属性或方法

# drf 的Response 源码分析
	导入Response
	from rest_framework.respone import Response
    
    # Response init可以传的参数
    def __init__(self, 
                 data=None, 
                 status=None,
                 template_name=None, 
                 headers=None,
                 exception=False, 
                 content_type=None)
    data: 之前我们编写的序列化类对象.data 
    	可以是字典列表,字符串 序列化后返回给前端
    status: http相应的状态码,默认为200
        我们可以通过导入相应状态码进行修改
    from rest_framework.status import HTTP_200_OK
    Response('abc',status=status.HTTP_200_OK)
    
    
    template_name:了解即可,修改响应模板的样子
    
    headers:响应头,http响应的响应头
        # 原生Django在相应头中携带数据
        obj = HttpResponse('dddd')
        obj['xxc'] = 'yyc'
        return obj
   content_type :响应编码格式,一般不动

视图组件介绍及两个视图基类

drf 视图 视图类
	APIView 是 drf的基类 drf提供最顶层的类
    
# APIView跟之前的View区别
    -传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
    -视图方法可以返回REST framework的Response对象-
    -任何APIException异常都会被捕获到,并且处理成合适的响应信息;
    -在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制    

APIView类-属性

APIVIew
    	-类属性:
            renderer_classes # 响应格式
    		parser_classes #能够解析的请求格式
    		authentication_classes#认证类
    		throttle_classes#频率类
    		permission_classes#权限类

基于APIView+ModelSerializer+Resposne写5个接口

# urls 路由层
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('book/', views.BookView.as_view()),
    path('book/<int:pk>/', views.BookDetailView.as_view()),
]
# views 视图层
from rest_framework.views import APIView
from app01 import models
from rest_framework.response import Response
from app01 import serializer
class BookView(APIView):
    def get(self, request):
        book_query_set = models.Book.objects.all()
        serializer_obj = serializer.BookSerializer(instance=book_query_set,many=True)
        return Response(serializer_obj.data)

    def post(self,request):
        serializer_obj = serializer.BookSerializer(data=request.data)
        if serializer_obj.is_valid():
            serializer_obj.save()
            return Response({"msg":'新增成功'})
        return Response({"msg":serializer_obj.errors})

class BookDetailView(APIView):
    def get(self,request,pk):
        book = models.Book.objects.filter(pk=pk).first()
        serializer_obj = serializer.BookSerializer(instance=book)
        return Response(serializer_obj.data)

    def put(self,request,pk):
        book = models.Book.objects.filter(pk=pk).first()
        serializer_obj = serializer.BookSerializer(instance=book,data=request.data)
        if serializer_obj.is_valid():
            serializer_obj.save()
            return Response({"msg": '修改成功'})
        return Response({"msg": serializer_obj.errors})

    def delete(self,request,pk):
        models.Book.objects.filter(pk=pk).delete()
        return Response()
# serializer 序列化类
from rest_framework import serializers
from app01 import models


class BookSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Book
        fields = ['name', 'price','publish', 'authors', 'publish_detail', 'author_list']  # 序列化反序列化字段
        extra_kwargs = {
            "publish": {"write_only": True},
            "authors": {"write_only": True},
            # 'publish_detail': {'read_only': True},  # dict
            # 'author_list': {'read_only': True},  # list
        }

    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

基于GenericAPIView+5个视图扩展类

视图类


from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin, \
    ListModelMixin


# 基于GenericAPIView+5个视图扩展类写接口

class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


class BookDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

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

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

序列化类

class BookSerializer(serializers.ModelSerializer):
    # 跟表有关联
    class Meta:
        model = Book
        fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors']
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'author_list': {'read_only': True},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        }

路由

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookDetailView.as_view()),
]