drf中-APIView/请求/响应

基于APIView+Response 写接口

在views.py中
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book


class BookView(APIView)# 使用cbv方法编写 继承APIView
    def get(self,request):
       book_list = []
       books = Book.objects.all() # 拿到所有数据对象
       for book in books:
          book_list.append({'name':book.name,'price':book.price})
       # 把数据对象转为字典 添加入一个列表
       return Response(book_list)
       # 使用drf内Response模块,可以直接将字典或列表序列化

分析APIView执行流程

from rest_framework.views import APIView
from rest_framework.response import Response

path('books/', views.BookView.as_view())

1.APIView的as_view方法执行:view还是原来的view,但是去掉了csrf的认证
当请求来了的时候会执行views.BookView.as_view()这个方法,因为BookView类本身没有as_view()方法,所以从父类里面查找,查到了APIView里面的as_view方法并执行了
 	@classmethod
  # 类方法 调用父类的as_view
   def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)
        # super是从父类中拿as_view(**initkwargs)方法并执行
        # 执行后获得了原生django中as_view中闭包函数view拿出来了
        return csrf_exempt(view)
        # csrf_exempt 排除所有请求的csrf认证
        # 最后相当于把原生view排除了所有csrf认证
        
2.路由匹配成功相当于执行了 csrf_exempt(view)(request),本质还是原生的view执行了self.dispatch
self是视图类对象(bookview对象由于bookview对象中没有dispatch方法,所以就从父类中找 APIView中找到了dispatch方法

    def dispatch(self, request, *args, **kwargs):
        request = self.initialize_request(request, *args, **kwargs)
        # 在这里把原来的request包装成了新的request 
        # 通过initialize_request方法看到把老的request放入了request._request中
        self.request = request
        # 把新的request放到了视图类的对象中
        try:
        # 开启异常捕获
            self.initial(request, *args, **kwargs)
            # 执行三大认证 认证 频率 权限

            if request.method.lower() in self.http_method_names:
              # 判断请求方式 是否在 self.http_method_names总
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
                
            response = handler(request, *args, **kwargs)

        except Exception as exc:
          # 如果执行过程中有异常 则捕获到
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
      
      
总结:1.先去除了所有请求的csrf认证
     2.包装了新的request 以后视图类中的request是新的了老的request在
     新的request._request里面
     3.在执行视图类方法之前 先进行了3大认证
     4.如果在执行3大认证或执行过程中出错 会有异常捕获

request源码分析

from rest_framework.request import Request

新的request中包含了老的  request._request就是老的

Request源码中:
        def __getattr__(self, attr):
        # __getattr__魔法方法:对象.属性如果该属性不存在则触发
        # request.get由于新的request中没有.method方法所以会触发这个魔法方法
        try:
            return getattr(self._request, attr)
            # 当这个方法被触发 就返回了老的request中的该方法
        except AttributeError:
            return self.__getattribute__(attr)
          
          
1.新的request用起来跟老的一样,是因为新的方法取不到就会默认取老的request的方法、
2.以后不管是什么数据,都从request.data中取,取出来就是字典
3.rul中携带的参数get请求携带的参数,以后从request.query_params中取
4.文件还是从request.FILES中取

class BookView(APIView):
    def get(self, request):
        print(self.request.data)
        print(self.request.query_params)
        print(self.request.FILES)

序列化组件(查询)

查询所有接口

首先创建序列化类

新建serializer.py文件:
from rest_framework import serializers
# 需要导入这个类 继承这个类
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    # 写法导致跟models类相同
    price = serializers.CharField()
    publish = serializers.CharField()
    # 需要序列化的字段数据,这里写什么就会自动序列化什么,如果有表中不想展示的字段,
    # 这里可以不写那个字段


path('books/', views.BookView.as_view()),
    
views.py中
    创建类方法 get请求获取所有数据
    class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        # 获得所有数据对象 queryset列表
        ser = BookSerializer(instance=books, many=True)
        # 把数据对象列表交给我们创建的序列化类进行处理,instance=需要序列化的数据
        # many=是否是多个数据,如果是单个数据对象不需要many属性
        return Response(ser.data)  
        # 直接把序列化好的数据返回,序列化好的数据在ser.data中

查询单个数据接口

查询单个url需要携带参数

 path('books/<int:id>', views.BookOneView.as_view()),

views.py中
 class BookOneView(APIView):
    def get(self, request, id):
        book = Book.objects.filter(pk=id).first()
        ser = BookSerializer(instance=book)
        # 由于book是单个数据 不需要many属性设置 默认many=False
        return Response(ser.data)

反序列化组件(新增/修改)

新增 
序列化文件中serializer.py中
添加类方法

path('books/', views.BookView.as_view()),

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

    def create(self, validated_data):
        book_obj = Book.objects.create(**validated_data)
        return book_obj
     # 创建create方法 并返回创建的对象
    
    
views.py视图类中添加post请求方法    
 class BookView(APIView):
    def post(self,request):
        ser = BookSerializer(data=request.data)
        # 将序列化类中的数据改为request.data
        # request.data就是通过校验前端传来的数据
        if ser.is_valid():
          # 当数据通过校验 直接保存
            ser.save()
            # 执行.save方法 该方法中会调用对象本身的create方法
            # 我们通过序列化类产生了一个对象,然后用这个对象调用create方法
            # 就是调用了序列化类里面的create方法我们刚刚写的
            return Response({'code':100,'msg':'新增成功'})
        else:
            return Response({'code':101,'msg':ser.errors})

          
修改:
 path('books/<int:id>', views.BookOneView.as_view()),
  
 
views.py视图类中添加put请求方法    
class BookOneView(APIView):
    def put(self,request,id):
        book = Book.objects.filter(pk=id).first()
        # 拿到数据对象
        ser = BookSerializer(instance=book,data=request.data)
        # 数据对象传入序列化类中,instance=需要修改的数据 data=前端传过来的数据
        # 产生一个新的数据对象
        if ser.is_valid():
            ser.save()
            # 执行.save()方法,其中通过判断是否有instance属性来判断是执行 create
            # 还是执行updata 有执行了updata
            return Response({'code':100,'msg':'修改成功'})
        else:
            return Response({'code':101,'msg':ser.errors})

          
在序列化类中添加 updata方法:
			  def update(self, instance, validated_data):
        # instance=需要修改的对象 我们之前传的book
        # validated_data 前端的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        instance.save()
        # 更改过后对象进行保存
        return instance
        # 记得然后在返回出去
        

删除资源

不涉及序列化或反序列化

class BookOneView(APIView):
    def delete(self,request,id):
        res = Book.objects.filter(pk=id).first()
        if not res:
            return Response({'code': 103, 'msg': '该文件不存在'})
        res = Book.objects.filter(pk=id).first().delete()
        if res:
            return Response({'code':102,'msg':'删除成功'})

反序列化的校验

反序列就是 新增 和 修改 等写入数据库的动作

在序列化的类中添加钩子函数做校验

主动抛异常需要导入函数 
from rest_framework.validators import ValidationError


class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里写要序列化的字典
    name = serializers.CharField()  # serializers下大致跟models下的类是对应的
    price = serializers.CharField()
    publish = serializers.CharField()

		# 局部钩子 name字段
    def validate_name(self, name):
        if len(name) < 3:
          # 对name字段做限制
            raise ValidationError('书名须大于3位数')
            # 主动抛异常
        else:
            return name
          # 没有问题还把这个字段反出去
          
    # 全局钩子
    def validate(self, attrs):
        if attrs.get('name') == attrs.get('publish'):
            raise ValidationError('书名和出版社名称不能一致')
        else:
            return attrs
          
    总结:如果有问题就主动抛异常
         如果没有问题还把数据返回出去

序列化跨表新增

前端数据:{"name":'moon',"phone":"110","info":{"addr":"上海",
                                        "work":"程序猿",
 }}
# 例如前端传入这样的数据  我们需要分别存入2张表  user userinfo
例如:

class UserSerializer(ModelSerializer):
     name = serializer.CharField()
     class Meta:
         model= User
         # 关联user表 
         fields=['name','phone','info'] 
         # 这里相当于需要用到的字段 前端传的我们都要用 都接受过来
	   info = serializer.DictField(read_only=True)
	   # 由于user表中没有info字段 所以需要自己写来接收
     
     def create(self, validated_data):
        info = validated_data.get('info')
        # 先把前端信息接收过来
        info_obj = UserInfo.objects.create(addr=info.get('addr'),word=info.get('word'))
        # 先创建出第二张表对象
        obj = User.objects.create(name=validated_data.get('name'),
                                  phone=validated_data.get('phone'),
                                  userinfo_id=info_obj.pk)
        # 然后在创建user表对象
         return book_obj
      
     
    
class UserView(APIView):

    def get(self,request):
        return Response('123')


    def post(self, request):
        ser = UserSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'msg': '添加成功'})
        else:
            return Response({'msg': '添加失败'})

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

在视图类中 
       ser = UserSerializer(data=request.data)
       if ser.is_valid():
      
当执行 is_valid 方法时 反序列化开始执行
校验通过则是 true  失败 false
    
is_valid 方法 查找顺序 先从序列化类中查找,没有找父类 再没有找父类的父类

is_valid部分源码展示:


   def is_valid(self, *, raise_exception=False):
    # raise_exception=False如果设置为true那出错就会直接抛异常
    # 然后我们可以利用异常捕获 
        if not hasattr(self, '_validated_data'):
        # 判断对象中是否有属性_validated_data
            try:
                # self序列化类的对象,属性中没有_validated_data,一定会走这句【核心】
            self._validated_data =  self.run_validation(self.initial_data)
            # 给对象中添加属性_validated_data 
            # 执行了run_validation方法 优先从对象中找,没有再找父类
            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)
        # 最后返回的就是true 和 false 如果有错误那就是false

        
-最终从Serializer类中找到了run_validation,而不是Field中的run_validation
    def run_validation(self, data=empty):
        (is_empty_value, data) = self.validate_empty_values(data)
        #  # 字段自己的,validates方法
        if is_empty_value:
            return data
        value = self.to_internal_value(data)
        # 这里执行了局部钩子
        try:

						# 这里在执行局部钩子,你如果没有那就在正常走

            value = self.validate(value)
            # 这里在执行全局钩子,如果全局钩子中抛异常 就会被正常捕获到
            # 全局钩子没有问题 那还是正常的流程 return出值
 
        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))
						
        return value
      
      
 .to_internal_value(data)源码:


     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)
            # 很精妙 通过反射来执行你在self对象中写的局部钩子
            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

就是判断值是否对,对就正常进行 不对就直接抛异常

name = input('请输入用户名')

assert name == 'moon'   # 断言name是moon 如果不是就会直接报错 抛异常
print('正常')
# 如果断言正确会正常执行

断言跟判断差不多,跟if差不多,这里如果不对就抛异常

drf之请求

Request源码-控制前端返回的编码格式

例如: 需求 该接口只能接受json格式的数据 不支持前端传入其他数据格式
from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
# 导入3种数据格式
parser_classes
# 解析前端闯入数据的类型

方式:局部控制
   class BookView(APIView):
       parser_classes = [JSONParser,]
       # 这样就实现了该接口只支持json格式
      # 通过该变量来控制可以接受的数据格式
   
  
  
  
  
  
需求:所有接口默认只支持json格式,个别接口需要支持其他格式

方法:全局控制
在django配置文件中settings 更新默认配置

    REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
       'rest_framework.parsers.JSONParser',
        # 'rest_framework.parsers.FormParser',
        # 'rest_framework.parsers.MultiPartParser',
    ],
}
  
  # 这样如果不使用局部控制,默认按照配置文件中设置的生效

# 如果全局配置和局部配置都有的话 生效局部配置的

def之响应

Response能够响应的编码格式

# drf  是djagno的一个app,所以要注册
# drf的响应,如果使用浏览器和postman访问同一个接口,返回格式是不一样的

from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer

JSONRenderer返回json格式
BrowsableAPIRenderer返回的http网页格式

# 这样可以控制响应类
#    class BookView(APIView):
       renderer_classes = [JSONRenderer,]
       # 这样配置会无论浏览器还是postman 都是返回json格式
    
# 这个设置一般是不用我们设置的

Response的源码和属性

	-from rest_framework.response import Response
  
  视图类的方法最后返回的是retrun Response()
  其实就是返回了一个Response实例化的对象,所以其实就是走了Response的
  __init__方法 init中可以传什么参数
  
  # Response init可以传的参数
    def __init__(self, 
                 data=None, 
                 status=None,
                 template_name=None, 
                 headers=None,
                 exception=False, 
                 content_type=None)
    
   data:就是我们返回的ser.data 可以是字典或者列表 或者字符串 直接序列化后给到前端
   status:是http响应状态码 默认响应成功显示200 你也可以改但没必要
   #eg:status=201
   -Response('dddd',status=status.HTTP_200_OK)
   # 如果有人这样写也可以理解一样的
   
   template_name=None,可以修改响应模板的样式 了解即可
    
   headers=None
  -Response('dddd',headers={'aaa':'111'})
   设置响应头 可以往响应头里面加东西 这样直接就在响应头里面加了东西
    
    
posted @   Python-moon  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示