APIView+Request源码分析,序列化器的使用,反序列化的校验

APIView和Response初见

APIView类

  • 是drf提供给咱们的一个类,以后使用drf写视图类,都是继承这个类及其子类。
  • APIView本身就是继承了Django原生的View

基于APIView+JsonResponse编写接口

# 原来基于django原生的View编写接口
# drf提供给咱们的一个类,以后使用drf写视图类,都是继承这个类及其子类,APIView本身就是继承了Django原生的View

class BookView(APIView):
    def get(self, request):
        books = Book.objects.all()
        book_list = []
        for book in books:
            book_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})

        return JsonResponse(book_list, safe=False)
from django.http import JsonResponse

class BookView(APIView):
    def get(self,request):
        books = Book.objects.all()
        book_list = []
        for book in books:
            book_list.append({'name':book.name,'price':book.price,'publish':book.publish})
        return JsonResponse(book_list, safe=False)




class BookDetailView(APIView):

    # 修改一个
    def put(self, request, pk):
        # 查到要改的
        book = Book.objects.filter(pk=pk).first()
        # 取出前端传入的数据,修改完再保存-----》    
        # 无论post,put。。放在body中提交的数据,都从request.data中取,取出来就是字典
        book.name = request.data.get('name')
        book.price = request.data.get('price')
        book.publish = request.data.get('publish')
        book.save()

        return JsonResponse({'id': book.id, 'name': book.name, 'price': book.price, 'publish': book.publish})

基于APIView+Response 写接口

from rest_framework.response import Response
class BookView(APIView):
  # 查询所有
def get(self,request): books = Book.objects.all() book_list = [] for book in books: book_list.append({'name':book.name,'price':book.price,'publish':book.publish}) return Response(book_list) # Response返回列表,不需要设置safe参数,无论列表还是字典都可序列化 class BookDetailView(APIView): # 修改一个 def put(self, request, pk): # 查到要改的 book = Book.objects.filter(pk=pk).first() # 取出前端传入的数据,修改完再保存-----》     # 无论post,put。。放在body中提交的数据,都从request.data中取,取出来就是字典 book.name = request.data.get('name') book.price = request.data.get('price') book.publish = request.data.get('publish') book.save() return Response({'id': book.id, 'name': book.name, 'price': book.price, 'publish': book.publish})

 

APIView执行流程

与CBV的源码分析过程一样,我们也是从路由中的as_view()作为切入点。

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

# APIView类的as_view内部是用了View的as_view内的view闭包函数
  • path的第二个参数是:APIView类的as_view
  • 一旦请求来了,匹配books路径成功,执行APIView的as_view()
  • 查看APIView的as_view方法,调用了原生的as_view(),但是加了个csrf_exempt装饰器
  • 因为APIView调用了原生的as_view(),所以还要研究原生的as_view()
  • self.dispatch(request) ,self指的是APIView,注意查找顺序,先找APIView中的dispatch方法

图解

# 路由中写的: path('books/', views.BookView.as_view()),---》请求来了,执行views.BookView.as_view()()----->现在的as_view是APIView的as_view

# APIView的as_view方法:view还是原来的view,但是以后再也没有csrf认证了
     @classmethod
       def as_view(cls, **initkwargs):
        # 调用父类的as_view,父类是django原生的View
        # 把djagno原生View的as_view方法中的闭包函数view拿出来了
        view = super().as_view(**initkwargs)
        # csrf_exempt 排除所有csrf的认证
        # 相当于在所有的方法上面加了这个装饰器
        return csrf_exempt(view)
# 重点研究APIView的dispatch方法   
# 路由匹配成功,执行 csrf_exempt(view)(requets)--->View的as_view中的闭包函数view---》self.dispatch---->self是视图类的对象---》BookiView---》APIView的dispatch,找到了
    def dispatch(self, request, *args, **kwargs):
        # request是django原生的request,老的request
        # 把老的request包装成了新的request,这个是drf提供的Request类的对象
        request = self.initialize_request(request, *args, **kwargs)
        # 到此以后,这个request就是新的了,老的request在哪?
        # request._request 这是老的
        
        # 把新的request放到了self对象【BookView的对象】
        self.request = request
        try:
            # 执行了三大认证【认证,频率,权限】,使用新的request
            self.initial(request, *args, **kwargs)
            
            # 跟之前一模一样
            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传入了,视图类的方法中get的request也是新的
            response = handler(request, *args, **kwargs)

        except Exception as exc:
            # 在执行3大认证和视图类中方法的过程中,如果出了异常,都能捕获到---》全局异常捕获
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

总结:

1 只要继承APIView之后都没有csrf的认证了

2 包装了新的request,以后在视图类中用的request是新的request对象,不是原生的了(原生的在新的requets._request中)

3 在执行视图类的方法之前,执行了3大认证——权限,频率,认证

4 如果在3大认证或视图函数方法执行过程中出了错,会有异常捕获——全局异常捕获

Request对象源码分析

  • django原生的request对象:django.core.handlers.wsgi.WSGIRequest
  • drf的request对象:rest_framework.request.Request
  • drf 的 request 对象中有原生的 request 对象 : self._request

图解:

 

疑问:在视图类中使用 request 对象, 从中取出想要的数据?

# 正常取值应该是使用 "_request"
request._request.method

# 但我们使用的却是
request.method

如何实现这种操作?

  • 对象.属性,当属性不存在时会触发类的__getattr__方法
  • drf的Request类重写了__getattr__
def __getattr__(self, attr):
    try:
        # 从原生的request中反射出要取的属性
        return getattr(self._request, attr)
    except AttributeError:
        return self.__getattribute__(attr)

在 Response 类中找到 __getattr__, 当属性在 drf 中的 request 中找不到时, 就会通过反射的方式从原生的 request 对象中取值

虽然视图类中request对象变成了drf的request,但是用起来,跟原来的一样,只不过它多了一些属性

  • request.datapost请求提交的数据,不论什么格式,都在它中
  • requst.query_paramsget请求提交的数据(查询参数)

总结:request的取值方法

1. request.data    # 这是个方法,包装成了数据属性
# 以后无论是post还是put请求,无论是哪种编码格式,放在body中提交的数据,都从request.data中取,取出来就是字典
       
2. request.query_params    # 这是个方法,包装成了数据属性
# get请求携带的参数,以后从这里面取
        
3. request.FILES    # 这是个方法,包装成了数据属性
# 前端提交过来的文件,从这里取

序列化器介绍和快速使用

序列化器的作用

  • 序列化, 序列化器会把模型对象转换成字典, 经过 response 以后变成json字符串
  • 反序列化, 把客户端发送过来的数据,经过 request 以后变成字典, 序列化器可以把字典转成模型

  • 反序列化, 完成数据校验功能

使用序列化器的执行流程

 

 使用APIView+序列化类+Response 编写接口

  • 创建一个serializer.py文件,定义一个序列化类BookSerializer,并继承Serializer
  • 在类中写要序列化的字段, 序列化字段类(有很多, 常用的就几个, 等同于models中的字段类)
  • 在views.py中导入序列化类,获取序列化对象
  • 得到序列化的数据,通过[序列化类的对象].data获取序列化后的字典

序列化多条

# models.py 创建模型类

from django.db import models

# Create your models here.

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)

# serializers.py 创建序列化类

from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    # 要序列化的字段有很多,每个字段也有很多字段属性
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

# urls.py开设路由

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

# views.py书写视图类

from rest_framework.views import APIView
from .models import Book
from rest_framework.response import Response
from .serializer import BookSerializer

class BookView(APIView):
    # 查询多条
    def get(self, request):
        books = Book.objects.all()  # 获取书籍对象
        # instance表示要序列化的数据,instance是queryset对象,many=True表示序列化多条
        ser = BookSerializer(instance=books, many=True)  # 构造序列化对象
        return Response(ser.data)  # ser.data获取序列化后的字典,通过Response转成json格式之后传给前端

序列化单条

  • 序列化类没有变,创建新的路由和视图类

# urls.py

path('books/<int:pk>/',views.BookDetailView.as_view())

# views.py

from rest_framework.views import APIView
from .models import Book
from rest_framework.response import Response
from .serializer import BookSerializer

class BookDetailView(APIView):
    # 查询单条
    def get(self, request, *args, **kwargs):
        book = Book.objects.filter(pk=kwargs.get('pk')).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)

 

反序列化

如果继承了Serializer, 数据的新增和修改, 调用 .save() 方法进行数据保存的时候都需要重写 create方法和 update方法。

反序列化的新增

# serializers.py

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.Serializer):
    # 要序列化的字段有很多,每个字段也有很多字段属性
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

  # 重写父类中的create方法   
def create(self, validated_data): # validated_data指校验过后的数据{name,price,publish} # 保存到数据库 book = Book.objects.create(**validated_data) return book  # 一定要返回新增对象

# views.py

from rest_framework.views import APIViewfrom rest_framework.response import Response
from .serializer import BookSerializer

class BookView(APIView):
    # 新增一条
    def post(self, request):
        ser = BookSerializer(data=request.data)  # 把前端传入的要保存的数据给data参数实例化得到ser对象
        if ser.is_valid():  # 前端传入的数据有可能不合规,要做数据校验(我们目前没有写校验规则)
            ser.save()  # 对象调用类BookSerializer的save方法,类中没有从父类中找
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})

反序列化的修改

# serializers.py

from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    # 要序列化的字段有很多,每个字段也有很多字段属性
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

  # 重写父类的updete方法
  def update(self, instance, validated_data):
        # instance是要修改的对象
        # validated_data指校验过后的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        instance.save()     # orm的单个对象。修改了单个对象的属性,只要调用对象的.save,就能把对象保存到数据库
        return instance

# views.py

from rest_framework.views import APIView
from .models import Book
from rest_framework.response import Response
from .serializer import BookSerializer

class BookDetailView(APIView):
    # 修改单条
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            '''对象调用save方法,查看save的源码可知,当instance有值执行update,没有值执行create'''
            ser.save()
            return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})

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

分析:视图类中新增与修改的.save()方法

  • BookSerializer()产生一个类ser
  • ser.save()表示的是对象调用类的方法,但是类BookSerializer中没有.save()方法
  • 到BookSerializer的父类中查找到.save()方法
  • 查看save的源码可知,当参数instance有值时执行update,没有值执行create
  • 根据对象中是否有instance参数,判断是新建还是修改

删除单条

# views.py

class BookDetailView(APIView):

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

反序列化的校验

# 序列化类反序列化,数据校验功能--->类比forms组件
    -局部钩子
    -全局钩子

案例

from rest_framework import serializers
from rest_framework.exceptions import ValidationError

class BookSerializer(serializers.Serializer):
# 反序列化校验的局部钩子,名字不能以sb开头
    def validate_name(self,name):
        # 校验name是否合法
        if name.startswith('sb'):
            # 校验不通过,抛异常
            raise ValidationError('不能以sb开头')
        else:
            return name

    # 全局钩子
    def validate(self, attrs):
        # 书名与出版社名不能一样
        if attrs.get('name') == attrs.get('publish'):
            raise ValidationError('书名与出版社名不能一样')
        else:
            return attrs

 

posted @ 2023-02-01 20:08  莫~慌  阅读(144)  评论(0编辑  收藏  举报