DRF之请求和响应
1 请求和响应
1.1 请求
rest_framework
传入到视图中的request对象
不再是Django默认的HttpResquest
对象,而是rest_framework
提供的扩展了HttpRequest类
的Request对象
from rest_framework.request import Request
# 对原生的request对象进行了二次封装
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
self._request = request #将原生的request对象赋值给了self._request
# 利用断言来判断传入的request是否是`django.http.HttpResponse`的实例
assert isinstance(request,HttpRequest),(异常信息)
若assert 后面的条件成立,则什么都不执行;若不成立,则抛异常
# 使用了代理模式
def __getattr__(self, attr):
# 如果Resquest对象没有attribute,我们将尝试代理底层的HttpResquest对象的属性
try:
return getattr(self._request, attr) # 利用反射来获取原生request对象的属性
except AttributeError:
return self.__getattribute__(attr)
@property # 伪装属性 获取POST的请求提交的数据(包括urlencoded,formdata,json等编码格式的数据)
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
def _load_data_and_files(self):
if not _hasattr(self, '_data'):
self._data, self._files = self._parse()
if self._files:
self._full_data = self._data.copy()
self._full_data.update(self._files)
else:
self._full_data = self._data
if is_form_media_type(self.content_type):
self._request._post = self.POST
self._request._files = self.FILES
# 获取get请求提交的数据
@property
def query_params(self):
return self._request.GET
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON
、表单
等)将请求数据进行parse解析,解析为类字典[QueryDict]对象
保存到Request对象中。
1.2 响应
REST framework 提供了响应类Response
,使用该类来构造响应对象的时候,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。
from rest_framework.response import Response
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
# data 你需要返回的数据,字典{‘msg’:'xxx','data':''}
# status 返回的状态码,默认是200
--from rest_framework import status, 在这个路径下,涵盖了基本状态码,如:HTTP_200_OK = 200 HTTP_201_CREATED = 201
# template_name 渲染的模板名称(自定义模板)
# headers: 响应头,字典 headers:{'X-CSRFToken':'{{csrf-token}}'}
# content_type: 响应编码的格式 [application/json] [text/html]
REST framework提供了Renderer
渲染器,用来根据请求头中的Accept
(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。
# 全局配置 在settings.py文件中配置REST FRAMEWORK
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES":(
'rest_framework.renderers.JSONRenderer', #json 格式渲染
'rest_framework.renderers.BrowsableAPIRenderer', #渲染 api浏览器
)
}
# 局部配置
# 在视图类中做如下的调整:
class Book1View(APIView):
renderer_classes = [JSONRenderer, ]
def get(self, request):
pass
def post(self, request):
pass
# DRF的配置信息:
先从自己类(局部)中找,没有的话去全局(settings.py)中去找,再没有的就采用默认的
2 视图(view)
REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。
2.1 基于APIView来写接口
APIView
是rest_framework
提供的所有视图的基类,它继承于Django
的View
from rest_framework.views import APIView
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from app01.models import BookModel
from app01.serializer import BookSerializer
class Book1View(APIView):
renderer_classes = [JSONRenderer, ]
def get(self, request):
book_list = BookModel.objects.all()
book_ser = BookSerializer(book_list, many=True)
return Response(book_ser.data)
def post(self, request):
# 新增没有instance,修改才有instance
book_ser = BookSerializer(data=request.data)
print(request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status':101,'msg':'数据校验失败'})
class Book1Detail(APIView):
renderer_classes = [JSONRenderer, ]
def get(self, request, pk):
book = BookModel.objects.filter(pk=pk).first()
if book:
book_ser = BookSerializer(book)
return Response(book_ser.data)
else:
return Response({'status': 101, 'msg': '无效的pk'})
def put(self, request, pk):
# 修改需要instance
instance = BookModel.objects.get(pk=pk)
book_ser = BookSerializer(instance=instance, data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status':101,'smg':'数据校验未通过'})
def delete(self, request, pk):
book = BookModel.objects.filter(pk=pk)
if book:
book.delete()
return Response({'status':200,'msg':'删除成功'})
else:
return Response({'status':101,'msg':'无效的pk'})
models.py
class BookModel(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32,verbose_name='书名')
price = models.DecimalField(max_digits=5,decimal_places=2,verbose_name='价格')
author = models.CharField(max_length=32,verbose_name='作者')
publish = models.CharField(max_length=32,verbose_name='出版社')
def __str__(self):
return self.name
class Meta:
verbose_name_plural='图书表'
serializer.py
class BookSerializer(serializers.Serializer):
book_id = serializers.CharField(source='id', read_only=True)
book_name = serializers.CharField(source='name', max_length=32)
book_price = serializers.CharField(source='price')
author = serializers.CharField(max_length=32)
publish = serializers.CharField(max_length=32)
def create(self, validated_data):
instance = BookModel.objects.create(**validated_data)
return instance
def update(self, instance, validated_data):
for attr, value in validated_data.items():
if hasattr(instance, attr):
setattr(instance, attr, value)
instance.save()
return instance
我们发现基于APIView书写的视图比较繁琐,那么有没有简便的方法了,接下来,我们一起看下基于GenericAPIView书写的视图
2.2 基于GenericAPIView来写接口
GenericAPIView
类继承于APIView
类,主要了增加了操作序列化器和数据库查询的方法,为下面的Mixin扩展类的执行提供方法支持,可以搭配一个或多个Mixin扩展类。
class Book2View(GenericAPIView):
renderer_classes = [JSONRenderer, ]
# queryset参数需要传queryset对象,查询了所有的图书
# serializer_class 使用序列化类来序列化这些数据
queryset = BookModel.objects # 类属性,对象调用,只能改自己的,不能改类属性的
serializer_class = BookSerializer
def get(self, request):
book_list = self.get_queryset() # Book.objects.all()
book_ser = self.get_serializer(book_list, many=True)
return Response(book_ser.data)
def post(self, request):
book_ser = self.get_serializer(data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status':101,'msg':' 数据校验失败'})
class Book2Detail(GenericAPIView):
renderer_classes = [JSONRenderer, ]
queryset = BookModel.objects
serializer_class = BookSerializer
def get(self, request, pk):
book = self.get_object()
book_ser = self.get_serializer(book)
return Response(book_ser.data)
def put(self, request, pk):
book = self.get_object()
book_ser = self.get_serializer(instance=book, data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status': 101, 'msg': '数据校验未通过'})
def delete(self, request, pk):
self.get_object().delete()
return Response({'status': HTTP_200_OK, 'msg': '删除成功'})
来看下GenericAPIView源码
queryset = BookModel.objects
serializer_class = BookSerializer
其实,就是self.queryset和self.serializer_class
# self是Book2View的实例化对象
# book_list = self.get_queryset()
def get_queryset(self):
queryset = self.queryset #BookModel.objects
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all() # BookModel.objects.all()
return queryset
# book_ser = self.get_serializer(book_list, many=True)
# BookSerializer(book_list, many=True)
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class() # BookSerializer
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs) # BookSerializer()
def get_serializer_class(self):
return self.serializer_class # BookSerializer
# book=self.get_object()
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
# Perform the lookup filtering.
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field # self.lookup_field = 'pk' or self.lookup_url_kwarg = None -->pk
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs) # 这里帮你做了pk不存在的检测
# May raise a permission denied
self.check_object_permissions(self.request, obj) # 权限校验
return obj # BookModel.objects.filter(pk=pk).first()
GenericAPIView
相较于APIView
扩展了操作序列化器和查询数据库的方法,但是相较于代码量并没有减轻多少,那么有没有更好的方法了。这就是下面5个视图扩展类的起源。
2.3 基于GenericAPIView和5个视图扩展类来书写接口
5个视图扩展类分别是:
from rest_framework.mixins import ListModelMixin, CreateModelMixin -->list(get all) -->create(post)
from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin -->retrieve(get pk)
-->update(put pk) -->destroy(delete pk)
class Book3View(GenericAPIView, ListModelMixin, CreateModelMixin):
renderer_classes = [JSONRenderer, ]
queryset = BookModel.objects
serializer_class = BookSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class Book3Detail(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
renderer_classes = [JSONRenderer, ]
queryset = BookModel.objects
serializer_class = BookSerializer
def get(self, request, pk):
return self.retrieve(request, pk)
def put(self, request, pk):
return self.update(request, pk)
def delete(self, request, pk):
return self.destroy(request, pk)
我们发现针对于增删改查4个操作,我们需要写两个类图类,因为get(有查询多个,有查询单个),我们把(get,post,put,delete
) 分为两类:
(1)不需要传入主键(pk)
<1>get获取所有的数据;
<2>post新增一条数据
(2)需要传入主键(pk)
<1>get获取单个数据
<2>put更新一条数据
<3>delete删除一条数据
那么,有没有可以把两个视图类整合的方案了。这里,就要用到了ModelViewSet
。
2.4 使用ModelViewSet
书写5个接口
from rest_framework.viewsets import ModelViewSet
class Book4View(ModelViewSet):
renderer_classes = [JSONRenderer, ]
queryset = BookModel.objects
serializer_class = BookSerializer
urls.py
# 使用ModelViewSet来写5个接口
path('books4/', views.Book4View.as_view(actions={'get': 'list', 'post': 'create'})),
path('books4/<int:pk>/', views.Book4View.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
2.5 源码分析ViewSetMixin
# ViewSetMixin 重写了as_view()方法
# 核心代码
def view(request, *args, **kwargs):
self = cls(**initkwargs) # book对象
self.action_map = actions # {’get':'list','post':'create'}
# Bind methods to actions
# This is the bit that's different to a standard view
for method, action in actions.items():
# exp: action=list method=get
handler = getattr(self, action) # 利用反射,获取list的内存地址地址
setattr(self, method, handler) # 将list的内存地址赋值给get,以后执行slef.get(),就相当于执行了slef.list()
2.6 继承于ViewSetMixin
类的视图类
class Book5View(ViewSetMixin, APIView):
def get_book_list(self, request):
book_list = BookModel.objects.all()
book_ser = BookSerializer(book_list, many=True)
return Response(book_ser.data)
def add_one_book(self, request):
book_ser = BookSerializer(data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status': 101, 'msg': '数据未通过'})
def get_one_book(self, request, pk):
book = BookModel.objects.filter(pk=pk).first()
if book:
book_ser = BookSerializer(book)
return Response(book_ser.data)
else:
return Response({'status': 101, 'msg': '无效的pk'})
def update_one_book(self, request, pk):
instance = BookModel.objects.filter(pk=pk).first()
book_ser = BookSerializer(instance=instance, data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status': 101, 'msg': '数据未通过校验'})
def delete_one_book(self, request, pk):
BookModel.objects.filter(pk=pk).delete()
return Response({'status': HTTP_200_OK, 'msg': '成功删除'})
来个简单粗暴版本!
class Book6View(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,
DestroyModelMixin):
renderer_classes = [JSONRenderer, ]
queryset = BookModel.objects
serializer_class = BookSerializer
def get_book_list(self, request):
return self.list(request)
def add_one_book(self, request):
return self.create(request)
def get_one_book(self, request, pk):
return self.retrieve(request, pk)
def update_one_book(self, request, pk):
return self.update(request, pk)
def delete_one_book(self, request, pk):
return self.destroy(request, pk)
3 各个类的继承关系
APIVIew
APIView--->View--->object
Book1View--->APIView--->View--->object
Book1Detail--->APIView--->View--->object
GenericAPIView
GenericAPIView--->APIView--->View--->object
Book2View--->GenericAPIView--->APIView--->View--->object
Book2Detail--->GenericAPIView--->APIView--->View--->object
- (
GenericAPIView
,ListModelMixin
,CreateModelMixin
)
Book3View ---> GeneriCAPIView --->APIView --->View --->object
---> ListModelMixin --->object
---> CreateModelMixin --->object
(GenericAPIView
,RetrieveModelMixin
, UpdateModelMixin
, DestroyModelMixin
)
Book3Detail ---> GeneriCAPIView --->APIView --->View --->object
---> RetrieveModelMixin --->object
---> UpdateModelMixin --->object
---> DestroyModelMixin --->object
ModelViewSet
Book4View-->ModelViewSet-->GenericViewSet-->GenericAPIView-->APIView-->View-->object
-->ViewSetMixin -->object
-->ListModelMixin -->object
-->CreateModelMixin -->object
-->RetrieveModelMixin -->object
-->UpdateModelMixin -->object
-->DestroyModelMixin -->object
- (
ViewSetMixin
,APIView
)
Book5View--->ViewSetMixin --->object
--->APIView--->View--->object
- (
ViewSetMixin
,GenericAPIView
,ListModelMixin
,CreateModelMixin
,RetrieveModelMixin
,UpdateModelMixin
,DestroyModelMixin
)
Book6View--->ViewSetMixin --->object
--->GenericAPIView--->APIView--->View--->object
--->ListModelMixin --->object
--->CreateModelMixin --->object
--->RetrieveModelMixin --->object
--->UpdateModelMixin --->object
--->DestroyModelMixin --->object