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)
基于APIView+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) # 无论是列表还是字典都可以序列化
APIView执行流程
与CBV的源码分析过程一样,我们也是从路由中的as_view()
作为切入点。
path('books/',views.BookView.as_view()) # APIView类的as_view内部是用了View的as_view内的view闭包函数
- path的第二个参数是:APIView类的as_view内的view闭包函数
- 一旦请求来了,匹配books路径成功
- 执行第二个参数view函数内存地址(requset),其实是执行View的as_view内的view闭包函数,但是加了个csrf_exempt装饰器
- 所以,继承了APIView的所有接口,都没有csrf的校验了
- 执行self.dispatch(request) request也是新的request
# 路由中写的: 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) # 路由匹配成功,执行 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 所有的csrf都不校验 2 包装了新的request,以后在视图类中用的request是新的request对象,不是原生的了(原生的在新的requets._request中) 3 在执行视图类的方法之前,执行了3大认证——权限,频率,认证 4 如果在3大认证或视图函数方法执行过程中出了错,会有异常捕获——全局异常捕获 5 以后视图类方法中的request都是新的了
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.data
:post
请求提交的数据,不论什么格式,都在它中requst.query_params
:get
请求提交的数据(查询参数)
总结: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)
序列化单条
- 序列化类没有变,创建新的路由和视图类
# 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)
反序列化
反序列化的新增
# serializers.py
from rest_framework import serializers from .models import Book class BookSerializer(serializers.Serializer): # 要序列化的字段有很多,每个字段也有很多字段属性 name = serializers.CharField() price = serializers.CharField() publish = serializers.CharField() 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() 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
标签:APIView,self,request,源码,import,序列化,view From: https://www.cnblogs.com/chen-ao666/p/17084035.html