drf学习

发布时间:2023年12月20日

APIView执行流程分析

1.在路由中:path('books/', views.BookView.as_view()),请求来了

2.先看 as_view()---->APIView的 as_view---》as_view执行结果跟之前一样,去除了csrf认证

    @classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs) # 调用父类的 as_view,view还是View的as_view
        # 以后所有请求,都不会做csrf认证了
        return csrf_exempt(view)

3.请求来了执行 views.BookView.as_view()(request)--->view(request)--->csrf_exempt(view)(request)--->内部核心---》return self.dispatch(request)

4.?self 是 APIView类的对象---》APIView没有dispatch---》APIView的dispatch,核心代码如下

    def dispatch(self, request, *args, **kwargs):
        # 后续的request都是 initialize_request 返回结果--》新的request--》drf的Requet类的对象
        request = self.initialize_request(request, *args, **kwargs)
        # 新的request放到了 self.request中---》self是BookView类的对象
        # 后续视图类的方法中 可以直接 self.request取出 当次请求的request对象
        self.request = request

        try:
            # 执行了三大认证:
            '''
            self.perform_authentication(request)
            self.check_permissions(request)
            self.check_throttles(request)
            '''
            self.initial(request, *args, **kwargs)

通过反射,去视图类中:BookView中执行跟请求方式同名的方法

            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
		   # request是新的Request类的对象了  get方法的第一个参数request也是新的
            response = handler(request, *args, **kwargs)

执行视图类的方法结束

            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

5.看self.initialize_request 是APIView的

    def initialize_request(self, request, *args, **kwargs):
        # 类实例化得到对象,传入一些参数
        # Request类--》drf提供的类
        from rest_framework.request import Request
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    

只要继承APIView,以后方法中得request都变了,成了 rest_framework.request.Request 的对象了

但是用起来,跟之前的一模一样
原来的是:django.core.handlers.wsgi.WSGIRequest 类的对象

总结:

?? ?1 以后视图类方法中得request对象,变成了新的request,它是rest_framework.request.Request 的对象了,但是用起来跟之前一样
? ? 2 把新的request对象,同时放到了 视图类的对象中 ?self.request = request ?后续从视图类中可以直接通过 self.request取出来
? ? 3 在执行视图类的方法之前,执行了三大认证
? ? 4 如果三大认证或视图类的方法执行出错,会有全局异常处理
? ? 5 以后所有的接口都去除了csrf认证

回顾装饰器

作用:在不改变原函数源代码和调用方式的基础上,为他增加新功能

import time


def add(a, b):
    time.sleep(1)
    return a + b


# 统计add的运行时间
start_time = time.time()
add(3, 4)
end_time = time.time()
print('运行时间是:', end_time - start_time)


# 写一个通用的,以后只要统计任意函数运行时间,都使用通用的--->改变了调用方式,不行
 def outer(func):
     start_time = time.time()
     func(3, 4)
     end_time = time.time()
     print('运行时间是:', end_time - start_time)

 outer(add)




def outer(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print('运行时间是:', end_time - start_time)
        return res

    return inner


# inner=outer(add)
# 调用inner,就是执行,inner内部包了 add函数,inner叫闭包函数
# inner() # 改变了调用方式

# 继续优化
add = outer(add)

res = add(3, 4)  # add现在本质调用 inner,参数传给 inner,inner要能接收a,b 并且有返回结果
print(res)


# 以后想装饰哪个函数 得按如下操作,麻烦
 def ee():
     print('ee')

 ee=outer(ee)
 ee()

# python提供了一个语法糖 @ ,以后只要按如下方式编写代码,就能实现上述功能
@outer  # 等同于  ee=outer(ee)   该语法糖会把被装饰器的函数ee,当做参数,传入outer,并且把outer的执行结果,返回赋值给ee
def ee():
    print('ee')


# 以后ee其实已经不是ee了,是inner了,执行ee本质在执行inner


#####上述是原理####

## 记忆
# 以后写它
def outer(func):
    def inner(*args, **kwargs):
        # 被装饰器函数,执行之前干事
        res = func(*args, **kwargs)
        #被装饰函数,执行之后干事情
        return res
    return inner

# 装饰某个函数
@outer
def add(a,b):
    time.sleep(1)
    return a+b

APIView再回顾

在执行视图类的方法之前干了一些事

  • 去除csrf
  • 包装新的requet
  • 在视图类中加入了 self.request
  • 执行三大认证

在执行视图类的方法之后干了一些事

  • 处理全局异常

本质就是做了装饰器的功能

Request对象分析(难--记总结)

分析APIVIew时,分析出,以后request都是新的request了,是drf提供的Request的对象

from rest_framework.request import Request

源码解析之 __init__--->老的request在新的内部---》request._request:

    def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
        # 传入的request是老的,django原生的request
        # 放到了self._request,self 是新的request类的对象
        self._request = request
        self._data = Empty
        self._files = Empty
    -什么时候调用的 __init__?
    	新的                                老的
    	-request = self.initialize_request(request, *args, **kwargs)
        				  老的
        	return Request(request)

以后用新的跟用老的一样,为什么?

?? ?新的 ?requet.method
? ? 新的 ? request.path

魔法方法:

?? ?在类内部,以 __开头 ?__结尾的方法, 在某种情况下会自动调用,他们称之为魔法方法
? ? 学过:__init__: ?类名() 自动触发
? ? ? ? ? __str__: ? print(对象) ?自动触发

__getattr__?

  • 对象.属性 ,属性不存在会触发

新的 requet.method用的时候,如果method不存在就会触发 Request类的 __getattr__

  • 逻辑肯定是:从老的request中取出,你想要的东西
    def __getattr__(self, attr):
        try:
            # 通过反射,去老的中取,能取到就返回,取不到,执行except代码,再取不到就报错
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

以后新的request中多了个属性 data---》前端post,put提交的请求体中得数据,都会放在request.data中,无论何种编码格式,它都是字典

总结:

  • 新的request中有老的requet, 在request._request
  • 新的request 多了data属性,客户端提交的请求体中得数据,无论以那种方式编码,都在request.data中
  • 其他的使用,跟之前老request一模一样
    • request.method
      request.path
      request.POST
      request.GET
      request.FILES

总结:

  • 原生django--》post--》提交数据,只能处理urlencoded和form-data编码,从request.POST中取
  • 原生djagno--》put--》提交数据,处理不了,需要我们自己从body中取出来处理
    ? ? ?? ?-分不同编码格式:
    • urlencoded ---》name=lqz&age=19-->字符串切割
    • json----》{"xxz":"xx","yyz":"yyy"}---》json.loads
  • 原生django不能处理json提交的数据,需要自己做(put,post)
    • json----》body中:{"xxz":"xx","yyz":"yyy"}---》json.loads
  • 新的request解决了所有问题
    • request.data

?

序列化类介绍

?序列化类(组件)可以干的事

  1. 序列化 ?qs对象,单个对象 做序列化给前端
  2. 反序列化数据校验:前端传入数据---》校验数据是否合法
  3. 反序列化---》前端传入数据,存到数据库中

继承APIView+Response实现 Publish的5个接口

class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        l = []
        for publish in publish_list:
            l.append({'name': publish.name, 'addr': publish.addr})
        return Response({'code': 100, 'msg': '查询所有成功', 'results': l})

    def post(self, request):
        # 如果是urlencoded编码,这种方式不行 publish = Publish.objects.create(**request.data)
        publish = Publish.objects.create(name=request.data.get('name'), addr=request.data.get('addr'))
        return Response({'code': 100, 'msg': '新增成功', 'results': {'name': publish.name, 'addr': publish.addr}})


class PublishDetailView(APIView):
    def get(self, request, pk):
        publish = Publish.objects.filter(pk=pk).first()
        return Response(
            {'code': 100, 'msg': '查询单条成功', 'results': {'name': publish.name, 'addr': publish.addr}})

    def put(self, request, pk):
        publish = Publish.objects.filter(pk=pk).first()
        publish.name = request.data.get('name')
        publish.addr = request.data.get('addr')
        publish.save()
        return Response({'code': 100, 'msg': '修改成功', 'result': {'name': publish.name, 'addr': publish.addr}})

    def delete(self, request, pk):
        Publish.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '删除成功'})

序列化类的使用

drf 提供的序列化器,实现 序列化,反序列化和数据校验

步骤:

1.写个py文件,叫serializer.py

2.写个类,继承serializers.Serializer

3.在类中写要序列化的字段

    class PublishSerializer(serializers.Serializer):
        # 写字段,要序列化的字段
        name = serializers.CharField()
        addr = serializers.CharField()
        id = serializers.IntegerField()

4.在视图类中使用,完成 ?序列化

  • 多条:
    • ser = PublishSerializer(instance=publish_list, many=True) ?
    • ser.data ?序列化后的数据
  • 单条:
    • ser = PublishSerializer(instance=publish) ?
    • ser.data ?序列化后的数据

序列化类快速使用

视图类

from .serializer import PublishSerializer


class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        ser = PublishSerializer(instance=publish_list, many=True)  # 如果序列化多条,要many=True
        return Response({'code': 100, 'msg': '查询所有成功', 'results': ser.data})

class PublishDetailView(APIView):
    def get(self, request, pk):
        publish = Publish.objects.filter(pk=pk).first()
        ser = PublishSerializer(instance=publish)  # 单个不写many=True
        return Response(
            {'code': 100, 'msg': '查询单条成功', 'results': ser.data})

序列化类

from rest_framework import serializers
class PublishSerializer(serializers.Serializer):
    # 写字段,要序列化的字段
    name = serializers.CharField()
    # addr = serializers.CharField()
    id = serializers.IntegerField()

路由

urlpatterns = [
    path('publish/', views.PublishView.as_view()),
    path('publish/<int:pk>', views.PublishDetailView.as_view()),
]

序列化类反序列化校验

序列化类可以做字段校验---》三层

	-第一层:字段自己的serializers.CharField(max_length=12,min_length=3)
    -第二层:局部钩子
        def validate_name(self, name):
            # 待校验的前端传入的name的数据
            if name.startswith("sb"):
                # 不行,抛异常
                raise ValidationError('不能以sb开头')
            return name
        
   -第三层:全局钩子--》前端多传的,这里不会有
	    def validate(self, attrs):
            print(attrs)
            # 多个字段同时校验
            # 出版社名和地址不能一样---》出版社前3个字不能和地址前3个字一样
            if attrs.get('name')[:3] == attrs.get('addr')[:3]:
                raise ValidationError('出版社名和地址不能一样')
            return attrs
        
        
# 使用步骤
	-写序列化类:写三层规则
    -视图类中:
        ser = PublishSerializer(data=request.data)  # 把待校验数据传入
        if ser.is_valid():  # 做数据校验---》三层
            print(ser.data)
        else:
            print(ser.errors)  # 没有校验通过,打印错误信息

序列化类保存

步骤:

1.在序列化类中,必须重写 create,完成真正的保存

       # 保存,必须重写create
        def create(self, validated_data):
            # validated_data 校验过后的数据---》多传的数据,在这没有
            publish = Publish.objects.create(**validated_data)
            return publish  # 不要忘了返回新增的对象---》后续会拿着这个对象做序列化  ser.data--->根据它做序列化的

2 在视图类中,数据校验通过后,调用ser.save()

?ser.save() ?# 使用序列化类保存--》会报错---》咱们没有指定保存到那个表--》必须重写create方法

修改使用步骤:

1 在序列化类中,必须重写 update,完成真正的修改

        def update(self, instance, validated_data):  # {name:上海出版社,add:上海地址}
            instance.name=validated_data.get('name')
            instance.addr=validated_data.get('addr')
            instance.save() # publish 对象的save---》保存到数据中
            return instance

2 视图类中

		ser = PublishSerializer(instance=publish, data=request.data)
		ser.save() # 虽然新增或修改都是调用save,但是内部做了判断

文章来源:https://blog.csdn.net/m0_71292438/article/details/135108335
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。