首页 > 其他分享 >DRF 05

DRF 05

时间:2023-02-04 23:12:48浏览次数:31  
标签:ser 05 self get 序列化 data class DRF

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

# 反序列化校验类,什么时候,开始执行校验呢?
	-只要在BookView视图类中调用 ser.is_valid(),就会执行校验,检验通过就会返回True,不通过返回False,将错误提示返回到前端
    
    # 入口:ser。is_valid()是序列化类的对象,假设序列化类是BookSerializer对象,所以我们要到BookSerializer去找is_valid,然后就会发现找不到,就到父类BaseSerializer中有;
    【raise_exception=True 】
代码如下:
	def is_valid(self, *, raise_exception=False):
    
        if not hasattr(self, '_validated_data'):
            try:
                # self序列化类的对象,属性中一开始肯定没有_validated_data,一定会走下面这句,下次再来的话就不会走这句了【核心重点】
                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)
# self._validated_data = self.run_validation(self.initial_data) 核心--》每次找的的时候self的时候要知道这是自己写的序列化类的对象 

——而且找run_validation不要直接点的方式去找,而在继承的类中去找,找不到就到它的父类中去找,不然就会找到filed的这个类中去
-- 它的执行顺序是 从下往上找,找不到就再往上
--最终从Serializer类中找到了run_validation 而不是filed中的run_validation

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就拿到正常反回,所以反回的value就是校验过后的数据
            --在源码中这个功能是没有写的,若果自己写了全局钩子,就就会执行自己写的,如果没有写就执行父类的全局钩子,但是父类中的全局够字没有写,所以也不会拦截校验的数据,而是直接返回。
            value = self.validate(value)
 
        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))

        return value
# --局部钩子
 value = self.to_internal_value(data)
    --self是自己系诶的序列化类对象,然后从下往上找
     def to_internal_value(self, data):
        ret = OrderedDict()
        errors = OrderedDict()
        fields = self._writable_fields
        # 在这里for循环就是 fields写在序列化类中一个个字段类的对象(name=CharFiled)
        for field in fields:
            #通过反射找self self BookSerializer的对象,如果在类里面写了validate_字段名,就会将这个字段名取出来,就会执行下面的这个方法,( 反射validate_name)
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            try:
                # 在执行BookSerializer类中的validate_name方法,传入了要校验的数据,这里就是为什么需要传入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
    
如果有异常就将异常加到了ValidationError中,所以我们才能点的方式那到错误信息
小知识扩展:

【在视图类的方法中写raise_exception=True,就不要写判断,如果校验不同过就会直接抛异常,那么程序就会终止,之前学过的全局捕获异常,可以做统一异常处理,到后期这里用一句话就可以,就不需要加if判断了 】

总结:

  • 反序列化类校验时通过is_valied开始执行的
  • is_valid是Serializer的父类BaseSerializer中的
  • 在Serializer类中找到了run_validation,执行里面的局部钩子/全局钩子

断言

# 断言,和try在源码中使用的较多
断言关键字: assert  
【说明一下:就是我断定你是什么,如果是xxx,那就没什么事,如果不是的话就抛出异常】

name="jason"
# if name == 'jason':
    # print('yes')
# else:
   # print('no')
 raise Exception ('断言的不对,不往下执行')

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

DRF --请求

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

能解析的编码格式三种

  • json格式
  • x-www-form- urlencoded
  • form-data

案列实现:

需求:要求这个接口只能接收json格式,不能接收其他格式的数据

方式一:
    通过继承APIView极其子类的视图类中配置(就是视图类自身)(局部配置)
    parser_classes:解析类  能够解析前端传入的数据格式
有三个:from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
	class BookView(APIView):
    parser_classes = [JSONParser,]
对应关系:
    json : JSONParser
    x-www-form-urlencoded:FormParser
    form-data:MultiPartParser

方式二:在配置文件中配置(影响真个项目,是全局配置)
--django中有套默认的配置,每个项目都有配置文件
--drf 也有套默认的配置,每个项目也有个配置,就在django的配置文件中
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        # 'rest_framework.parsers.FormParser',
        # 'rest_framework.parsers.MultiPartParser',
    ],
}

方式三: 全局配置了一个,某一个视图类中想要三个
	- 在视图类中配置三个即可
    - 因为:执行功能的方式是会先从自身开始找。找不到就会到项目的drf配置文件中找,再找不到就到drf默认的配置中去找

补充小知识:
    jason 格式传入的数据是普通字典
    FormParser,MultiPartParser,传入的数据是queryDict 
    django.http.request.QueryDict它是继承了Dict
    getlist :传进来的数据是 K:Y的格式

总结:

  • 解析前端传入的数据有三种格式
  • 可以在自己写的视图类中通过parser_classes = 来定制
  • 还可以在项目的配置文件中配置
  • 在配置文件配置的是全局生效的,想要单独给某一个视图类定制就需要在视图类中配置就行
  • 如果在两个地方都配置了,执行顺序就是先执行自身的,没有再执行配置文件中的

DRF --响应

Response能够响应的编码格式

drf 是Django的一个App,我们在浏览器执行的时候就会报错,所以我们要在settings中去注册
	INSTALLED_APPS = [
    'app01.apps.App01Config',
    'rest_framework'
]

#drf的响应:
	使用浏览器和postman的访问同一个接口,返回的数据格式是不一样的
    -drf的底层 做了一个判断,如果是浏览器就做的好看些,如果是postman就只要json数据
    --renderer_classes: 渲染的类
    
默认渲染的class
	DEFAULT_RENDERER_CLASSES: [
    # json的render
        'rest_framework.renderers.JSONRenderer',
 # 浏览器的API的render 
        'rest_framework.renderers.TemplateHTMLRenderer',
    ],

image

控制响应的编码格式

通过视图类到drf的配置文件中找到两个类
方式一:在视图类中写(局部配置)
	-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.TemplateHTMLRenderer',
    ],
}
方式三:执行的顺序(默认使用内置即可)
	优先使用视图类中自身的配置,其次使用项目配置文件中配置,最后再使用内置的
	

Response的源码和属性

drf Response的源码分析
from rest_framework.response import Response
 --通过看源码,了解到Response继承SimpleTemplateResponse ,而他继承了HttpResponse
  --视图类返回方法的时候,return Response,走的是Response的__init__,这里需要关注的就是__init__中可以传什么参数

class Response(SimpleTemplateResponse):
    def __init__(self, 
                 data=None, 
                 status=None,
                 template_name=None,
                 headers=None,
                 exception=False,
                 content_type=None):
--data :之前我们写的ser.data 可以是字典或列表。字符串,它会序列化后返回给前端,所以前端在响应体(body体中)中看到的就是这个。

--status :这是响应的状态码,默认是200,可以自己修改
	drf 在status这个包下面,把所有的http响应状态码都写了一遍
    from rest_framwork.status import HTTH_200_OK
    return Response('jason',status=status.HTTP_200_OK)

--template_name: 修改相应模板的样子,BrowsableAPIRender 是固定的样子,后期可以根据公司自己定制(了解即可)

--headers: 响应头 http响应的响应头
	小问题:原生django中怎么在响应投资中加数据?(这会涉及到跨域问题)
    # 四件套 render redirect,HttpResponse,JasonResponse 返回的数据的时候都有一个接受的对象
        obj=HttpResponse('aaa')
        obj['xx']='yy'
        return obj	

content_type: 响应状态码的格式,一般默认即可

image

总结:

  • 上面就讲了两种,响应状态码和状态码属性
  • 其中重要的就是,data,headers,status

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

  • 讲的就是drf的视图,视图类,之前学过的APIView是drf的基类,是一个drf 提供的最顶层的类

APIView和之前的View的区别

	1.传入到视图的方式是RESR framework的Response对象,而不是django的HttpResponse对象
	2.视图方法可以返回REST framework的(drf的)Response对象
    3.任何APIException异常都会被捕获到,并且处理成合适的响应信息
    4.再进行dispatch()分发前,会对请求进行身份认证,权限检查,流量控制(频率)

两个视图基类

# APIView 视图类
	会使用到的类属性:
        renderer_classes :响应格式 
        parser_classes :能够解析的请求格式 
        authentication_classes :认证类
        throttle_classes :评率类 
        permission_classes :权限类

	基于APIView+ModelSerializer+Response 写五个接口
    

APIView+ModelSerializer+Response 写五个接口 代码

视图类
### 视图类基于APIView写五个接口 第一层 初级层
class BookView(APIView):

    def get(self,request):
        books=Book.objects.all()
        ser=BookSerializer(instance=books,many=True)
        return Response(ser.data)

    def post(self,request):
        ser=BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            # 现在只有序列化的对象,但是我们想要新增的对象序列化成字典
            # 前提是和序列化类中BookSerializer的create方法一定要返回新增的对象
            return Response({'code':100,'msg':'新增成功','result':ser.data})
        else:
            return Response({'code':101,'msg':ser.errors})

class BookViews(APIView):
    def get(self,request,pk):
        books=Book.objects.filter(pk=pk).first()
        ser=BookSerializer(instance=books)
        return Response(ser.data)


    def put(self,request,pk):
        books=Book.objects.filter(pk=pk).first()
        ser=BookSerializer(instance=books,data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code':100,'msg':'修改成功'})
        else:
            return Response({'code':101,'msg':ser.errors})


    def delete(self,request,pk):
        Book.objects.filter(pk=pk).delete()
        return Response('删除成功')
序列化类
class BookSerializer(serializers.ModelSerializer):
    #  和表建立关系
    class Meta:
        model = Book  # 序列化类和表建立了关系
        fields = ['name', 'price', 'publish', 'author_list', 'publish_detail', 'authors']
        extra_kwargs = {'name': {'max_length': 8},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True},
                        'publish_detail': {'read_only': True},
                        'author_list': {'read_only': True}
                        }

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

]
  • 根据代码分析,我们可以得知,我们写的是一个book的接口,如果我们要写的是publish的接口,用的视图类代码就是上面的代码改改一部分就可以快速写出publish的五各接口,他们的区别就是在表模型和序列化类
  • 考虑能不能想一种方式,通过继承的方式,少写代码
  • 就是GenerricAPIView,他继承了APIView有一些新的属性和方法;以后可以基于这个类来写五个接口

基于GenerricAPIView 写五个接口

  • 代码是差不多的,只不过基于APIView之上多写了几个属性和方法
五个接口的效果还是一样的,但是代码的可用性变高了
这里面有两个重要的属性和方法
属性:
1.queryset : 这是要序列化或反序列化的模型数据
serializer_class :使用的序列化类
lookup_field='pk'  查询单条数据的路由,分出来的字段名
 filter_backends : 过滤类的配置(了解)
 pagination_class: 分页类的配置(了解)
 
 方法:
 get_queryset  获取要序列化的对象
 get_object   获取要序列的单个对象
 get_serializer  获取序列化类 还有和它差不多的,一般不调用它,会重写它
 filter_queryset  他和后续的过滤有关系(了解)
'''

image
image

通过看原代码可以了解到:

# queryset=Book.objects.all() # 这里的all已经在源码中return出来了,所以可以省略不写
    queryset=Book.objects
    serializer_class = BookSerializer
        # obj=self.queryset # 这里提供了可以拿到的方法,但是不要这么使用,
        objs=self.get_queryset()
        '''
        这么写有什么好处呢?
        这是一个方法,我们可以在这个类中去重写这个方法,给它剔除点数据,或者假如点数据,返回点别的
        那么他序列的就可以不一样了。它的可扩展性高
        如果直接拿,拿到的数据就是写死的,
        def get_queryset(self):的话就能重写着个方法
        '''
        ser=self.get_serializer()
 看了原代码发现,serializer_class,又套了一层
	serializer_class = self.get_serializer_class()
 这越套它的扩展性就会越高,它这里做了什么事呢? 执行了上面的那句调用了get_serializer_class(),而get_serializer_class()就只是返回了要序列化的序列化类,然后拿到传进来的序列化类的东西,传进了serializer_class
同理,后期重写get_serializer_class,因为它返回那个序列化类,serializer_class就会拿哪个序列化类序列化

因为后期我们可能跟会使用到的序列化类不一样,序列化类和反序列化类,我们不能知己而在serializer_class =  写什么或许什么,所以我么就可以通过重写这个方法,来控制,我们使用那个序列化类做序列化
代码:
from rest_framework.generics import GenericAPIView

class BookView(GenericAPIView):
    queryset = Book.objects
    serializer_class = BookSerializer

    def get(self, request):
    objs = self.get_queryset()
        # 使用 get_serializer的好处就是以后可以重写get_serializer_class指定哪个序列化类序列化
        ser = self.get_serializer(instance=objs, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            # 现在只有序列化的对象,但是我们想要新增的对象序列化成字典
            # 前提是和序列化类中BookSerializer的create方法一定要返回新增的对象
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})


class BookViews(GenericAPIView):
    queryset = Book.objects
    serializer_class = BookSerializer
    lookup_field = 'pk'

    def get(self, request, pk):
        obj = self.get_object()  # 获取单条数据拿的是数据对象 这里不加pk的原因是get_object方法中制定了
        ser = self.get_serializer(instance=obj)
        return Response(ser.data)

    def put(self, request, pk):
        obj = self.get_object()
        ser = BookSerializer(instance=obj, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 101, 'msg': ser.errors})

    def delete(self, request, pk):
        self.get_object().delete()
        return Response('删除成功')

  • 根据上述代码,能够知道,我们只需要修改那两个属性,其他的代码不用变就能够再写出其他模型表的类的五个接口
  • 如果我们写publish类属性的五个接口,继承字GeneralAPIView,跟Book的区别只有那两个类属性 ,
  • 通过这点我门得出一个结论,它还能封装。
  • drf的思路是封装了五个视图扩展类--->get 所有,get一个,post 获取,put修改,delete 删除
通过思路想到,写五个类,每一个类利旧一个方法,想用那个方法就继承那个方法,为什么不将五个类写到一起呢,因为有的时候我们可能不会都会使用得到这五个接口。

drf中它的五各视图扩展类他不是视图类。没有继承APIView极其子类,所以它不能但单独使用,必须配合GeneralAPIView一起才能使用。

五个类名:
	from rest_framework.mixins import CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin,ListModelMixin
    
    CreateModelMixin:新增
    UpdateModelMixin:更新
    DestroyModelMixin:删除
    RetrieveModelMixin:查单条
    ListModelMixin	   :查所有

是图类代码:
'''基于GeneralAPIView + 五个扩展类写接口'''

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

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

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


class BookViews(GenericAPIView, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin):
    queryset = Book.objects
    serializer_class = BookSerializer
    lookup_field = 'pk'

    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.delete(request, *args, **kwargs)

  • 观察上述代码,通过继承GeneralAPIView + 五个扩展类以后写的代码,如果后面还要写其他的模型类,只需要将代码复制,最后改下面的两个属性就可以了

    • queryset
      serializer_class
  • 那么就想着,在这个的基础之上再进行封装,将所有的扩展类封装成一个父类,最后使用变成 9个类

  • from rest_framework.generics import ListAPIView,CreateAPIView,ListCreateAPIView
    from rest_framework.generics import DestroyAPIView,UpdateAPIView,RetrieveAPIView,\
        RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView
    
class BookView(这里继承的是自己写的封装类):
    queryset = Book.objects
    serializer_class = BookSerializer

from rest_framework.viewsets import ModelViewSet

五个接接口使用同一个路由就会发现有点问题,因为有连个get,但是drf写了个魔法类,就是上面的这个
class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    
    pass

补充小知识:

ser.errors所有的错误都放到了这个序列化类的errors字典里面

标签:ser,05,self,get,序列化,data,class,DRF
From: https://www.cnblogs.com/qiguanfusu/p/17092598.html

相关文章