Drango REST framework 视图的使用
-
视图
-
APIView
-
GenericAPIView
-
Mixin扩展
-
-
视图集
-
概念
-
ModelViewSet
-
-
路由
-
使用
-
Request
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象
REST framework 提供了Parser解析器,在接受到请求后会自动根据Content-Type指明的请求数据类型(JSON、表单等)将请求数据进行parse解析,解析为类字典对象保存到Request对象中。
Request对象的数据是自动根据前端发送的数据格式进行解析后的结果。
现在,无论前端发送的是哪种格式的数据,我们哦度可以用统一的方式读取数据。
常用属性
1)data
request.data 返回解析后的请求图数据。类似于Django中表中的 request.POST 属性,但提供以下特性:
-
包含了对POST\PUT\DELETE\PATCH请求方式解析后的数据
-
利用了REST framework的parsers解析器,不仅支持表单数据,也支持JSON数据
2)query_params
request.query_params 与Django标准的 request.GET 属性相同。
Response
REST framework提供了一个响应类 Response ,使用该类构造响应对象的时候,响应的具体数据内容会被转换(render渲染)成符合前端需要的类型。
REST framework提供了Renderer渲染器,用来根据请求头中的Accept(接受数据类型声明)来自动转换响应数据到对应的格式。如果前端请求中未指明Accept,则会采用默认方式处理响应数据。我们也可以通过配置来修改默认响应格式。
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器 ) }
构造方式
Response(data,status=None,template_name=None,headers=None,content_type=None)
data 只需传递python的内建类型数据即可
data 不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用序列化器序列化后(传为了python字典类型)再传递给data参数。
参数说明:
-
data:为响应准备的序列化处理后的数据
-
status:状态吗,默认200
-
template_name:模板名称,如果使用HTMLRenderer时需要指明
-
headerrs:用于存放放响应头信息的字典
-
content_type:响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数
常用属性
1)data
传给response对象的序列化后,但尚未render处理的数据
2)status_code
状态吗的数字
状态码
为了方便设置状态码,REST framewrok在rest_framework.status
模块中提供了常用状态码常量。
1)信息告知 - 1xx
HTTP_100_CONTINUE HTTP_101_SWITCHING_PROTOCOLS
2)成功 - 2xx
HTTP_200_OK HTTP_201_CREATED HTTP_202_ACCEPTED HTTP_203_NON_AUTHORITATIVE_INFORMATION HTTP_204_NO_CONTENT HTTP_205_RESET_CONTENT HTTP_206_PARTIAL_CONTENT HTTP_207_MULTI_STATUS
3)重定向 - 3xx
HTTP_300_MULTIPLE_CHOICES HTTP_301_MOVED_PERMANENTLY HTTP_302_FOUND HTTP_303_SEE_OTHER HTTP_304_NOT_MODIFIED HTTP_305_USE_PROXY HTTP_306_RESERVED HTTP_307_TEMPORARY_REDIRECT
4)客户端错误 - 4xx
HTTP_400_BAD_REQUEST HTTP_401_UNAUTHORIZED HTTP_402_PAYMENT_REQUIRED HTTP_403_FORBIDDEN HTTP_404_NOT_FOUND HTTP_405_METHOD_NOT_ALLOWED HTTP_406_NOT_ACCEPTABLE HTTP_407_PROXY_AUTHENTICATION_REQUIRED HTTP_408_REQUEST_TIMEOUT HTTP_409_CONFLICT HTTP_410_GONE HTTP_411_LENGTH_REQUIRED HTTP_412_PRECONDITION_FAILED HTTP_413_REQUEST_ENTITY_TOO_LARGE HTTP_414_REQUEST_URI_TOO_LONG HTTP_415_UNSUPPORTED_MEDIA_TYPE HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE HTTP_417_EXPECTATION_FAILED HTTP_422_UNPROCESSABLE_ENTITY HTTP_423_LOCKED HTTP_424_FAILED_DEPENDENCY HTTP_428_PRECONDITION_REQUIRED HTTP_429_TOO_MANY_REQUESTS HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
5)服务器错误 - 5xx
HTTP_500_INTERNAL_SERVER_ERROR HTTP_501_NOT_IMPLEMENTED HTTP_502_BAD_GATEWAY HTTP_503_SERVICE_UNAVAILABLE HTTP_504_GATEWAY_TIMEOUT HTTP_505_HTTP_VERSION_NOT_SUPPORTED HTTP_507_INSUFFICIENT_STORAGE HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
APIView
rest_framework.views.APIView
APIView 是REST framework提供的所有视图的基类,继承自Django的 View父类
APIView 和 View 的不同之处在于:
-
传入到视图方法中的是REST framework 的Request对象,而不是Django的HttpRequest对象。
-
视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式。
-
任何APIException异常都会被捕捉到,并且处理成合适的响应信息。
-
在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
支持定义的属性:
-
authentication_calsses列表或元组,身份认证类
-
permissoin_classes列表或元组,权限检查类
-
throttle_classes列表或元组,流量控制类
在APIView中仍然以常规的类视图定义方法来实现get(),post()或者其他请求方式的方法。
举例:
from rest_framework.views import APIView from rest_framework.response import Response # url(r'^books/$', views.BookListView.as_view()), class BookListView(APIView): def get(self, request): books = BookInfo.objects.all() serializer = BookInfoSerializer(books, many=True) return Response(serializer.data)
GenericAPIView
rest_framework.generics.GenericAPIView
继承自APIView,在原有的APIView的继承上增加了对于列表视图和详情视图可能用到的的通用支持方法,通用使用时,可搭配一个或多个Mixin扩展类。
使用GenericAPIView类一般需要实现queryset属性或者重写get_querysett方法
支持定义的属性:
-
列表视图与详情视图通用:
-
queryset 列表视图的查询集
-
serializer_class 视图使用的序列化器
-
-
列表视图使用:
-
pagination_class 分页控制类
-
filter_backends 过滤控制后端
-
-
详情视图使用:
-
lookup_filed 查询单一数据库对象时候使用的条件字段,默认为pk
-
lookup_url_kwarg 查询单一数据的时候URL中的参数关键字名称,默认与look_filed相同
-
提供的方法:
-
列表视图与详情视图通用
-
get_queryset(self)
返回视图使用的查询集,是列表视图和详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:
def get_serializer_class(self): if self.request.user.is_staff: return FullAccountSerializer return BasicAccountSerializer
-
get_serializer_class(self)
返回序列化器类,默认返回serializer_class,可以重写,例如:
def get_serializer_class(self): if self.request.user.is_staff: return FullAccountSerializer return BasicAccountSerialzer
-
get_serializer(self,*agrs,**kwargs)
返回序列化器对象,或被其他试图或者扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
注意:在提供序列化器对象的时候,REST framework会向对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器的时候使用。
-
-
详情视图使用:
-
get_object(self)返回详情视图所需的模型类数据对象,默认为lookup_filed参数来过滤queryset,在视图中可以调用此方法来获取详情信息的模型类对象。
若详情访问的模型类对象不存在,则会返回404.
该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。
-
例如:
class BookDetailView(GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerialzer def get(sefl,request.pk): book = self.get_object() serializer = self.get_serializer(book) return Response(serializer.data)
Minxin
1)ListModelMixin
列表视图扩展类,提供list(request,*args,**kwargs)方法快速实现列表视图,返回200状态码。
该Mixin的list方法会对数据进行过滤和分页
源代码:
class ListModelMixin(object): """ List a queryset. """ def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
实际使用:
from rest_framework.mixins import ListModelMixin from rest_framework.generics import GenericAPIView class ListModelView(ListModelMixin,GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self,request): return self.list(request)
2)CreateModelMixin
创建列表视图扩展类,提供create(request,*args,**kwargs)方法实现快速创建的视图,成功返回201状态码。
如果序列化器对前端的发送数据验证失败,返回400错误。
源代码:
class CreateModelMixin(object): """ Create a model instance. """ def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer): serializer.save() def get_success_headers(self, data): try: return {'Location': str(data[api_settings.URL_FIELD_NAME])} except (TypeError, KeyError): return {}
实际使用:
from rest_framework.mixins import CreateModelMixn class CreateModelView(CreateModelMixin,GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def post(self,requset) return self.create(request)
3)RetrieveModelMixin
详情视图扩展类,提供retrieve(request,*args,**kwargs)方法,可以快速获取一个存在的数据对象
如果存在,返回状态码200,否则的话会返回404。
源代码:
class RetrieveModelMixin(object): """ Retrieve a model instance. """ def retrieve(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data)
实际使用:
from rest_framework.mixins import RetrieveModelMixin class RetrieveModelView(RetrieveModelMixin,GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self,request,pk=None): return self.retrieve(request)
4)UpdateModelMixin
更新详情视图扩展类,提供update(request,*args,**kwargs)方法,可以快速实现更新一个存在的数据对象。
同时也提供partial_update(request,*args,**kwargs)方法来实现局部更新。
成功的话会返回200,序列化器校验数据失败会返回400错误。
源代码:
class UpdateModelMixin(object): """ Update a model instance. """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} return Response(serializer.data) def perform_update(self, serializer): serializer.save() def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.update(request, *args, **kwargs)
实际使用:
from rest_framework.mixins import UpdateModelMixin class UpdateModelView(UpdateModelMixin,GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def put(self,request,pk) return self.update(request)
5)DestroyModelMixin
删除详情视图扩展类,提供destroy(request,*args,**kwargs)方法,可以实现快速删除一个存在的数据对象。
成功返回200状态码他,不存在的话返回状态404。
源代码:
class DestroyModelMixin(object): """ Destroy a model instance. """ def destroy(self, request, *args, **kwargs): instance = self.get_object() self.perform_destroy(instance) return Response(status=status.HTTP_204_NO_CONTENT) def perform_destroy(self, instance): instance.delete()
实际使用:
from rest_framework.mixins import DestroyModelMixin class DestroyModelView(DestroyModelMixin,GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerialzer def delete(self,request,pk): return destroy(request)
以下是几个可用的子类视图(相当于上面视图的简化)
####
列表视图:
1) CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
2)ListAPIView
提供 get 方法
继承自:GenericAPIView、ListModelMixin
详情视图:
3)RetireveAPIView
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
4)DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7)RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
举例:
from rest_framework.mixins import RestrieveUpdateDestroyAPIView class DetailModelView(RestrieveUpdateDestroyAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerialzer def get(self,request,pk=None): return self.retrieve(request) def put(self,request,pk) return self.update(request) def delete(self,request,pk): return destroy(request)
视图集ViewSet
Django REST框架允许将一组相关视图的逻辑组合到一类中,称为ViewSet
一个ViewSet类只是一种基于类的View,它不提供任何方法处理程序(如get()或者post()等),它很聪明的提供如list()和create()之类的操作。
使用ViewSet类比使用View类有两个主要的优点:
-
重复的逻辑可以合并为一个类。例如我们只需要指定queryset一次,它就将会自动用于多个视图。
-
通过使用路由器,我们不再需要自己处理自己的URL配置。
ViewSet
一个ViewSet类只是一种基于类的View,继承字APIView,作用也与APIView基本相似,提供了身份验证、权限验证和流量管理等。
在ViewSet中,它不提供任何方法处理程序,需要我自己重写该类并明确实现action方法。
class BookViewSet(ViewSet): def list(self,request): pass def create(self,request): pass def retrieve(self,request,pk=None): pass def update(self,request,pk=None): pass def partial_update(self,request,pk=None): pass def destroy(self,request,pk=None): pass
一个ViewSet类同时提供以下可用属性
-
basename : 用于创建的URL名称的基础
-
action :当前动作的名称(例如:list,create)
-
detail :布尔值,指示当前操作是否为详情或列表视图配置
-
suffix :视图类型的显示后缀 ,镜像 detail 属性
例如:
from rest_framework.viewsets import ViewSet from django_shortcuts import get_object_or_404 from rest_framework.response import Response class BookViewSet(ViewSet): def list(self,request): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer(queryset,many=True) return Response(serializer.data) def retrieve(self,request,pk=None): queryset = BookInfo.objects.all() user = get_object_or_404(queryset,pk=pk) serializer = BookInfoSerialzer(user) return Response(serializer.data)
在设置路由时,需要进行如下操作:
from django.conf.urls import url,include from . import views urlpatterns = [ url(r'^Books/$,views.BookViewSet.as_view({'get':'list'})), url(r'^Books/(?P<pk>\d+)/$,views.BookViewSet.as_view({'get':'retrieve'})), ]
还可以用以下的方式设置路由:
from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r'^books',views.BookViewSet,basename='books') urlpatterns = [ url(r'^',include(router.urls)) ] 或者 urlpatterns ++ router.urls
注意:在通常情况下,我们经常使用提供默认行为集的现有基类,例如ModelViewSet
GenericViewSet
继承自GenericAPIView,作用也与GenericAPIView相似,提供了get_object、get_queryset等方法便于列表视图与详情视图的开发。在GenericAPIView中,没有提供任何动作action方法,需要我们自己覆盖该类并混合所需的混合类,或者明确定义操作实现action方法。
ModelViewSet
继承自GenericAPIView,包括用于各种动作实现方式中,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
由提供的动作ModelViewSet类是
.list()
,.retrieve()
,.create()
,.update()
,.partial_update()
,.destroy()
使用:
因为ModelVieSet扩展GenericAPIView,所以我们通常至少需要提供queryset和serilazer_class属性
class BookListModelViewSet(ModelViewSet): queryset = BookInfo.objects.all() serializer_class = BookInfoSerialzer
这设置路由时,我们可以如下操作
router = DefaultRouter() router.register(r'books',views.BookLsitModelViewSet)
urlpatterns = [
url(r'^',include(router.urls))
]
视图集中定义附加action动作
如果你有额别的方法是应该是可路由的,你可以用@action装饰器来标记它们。像常规操作一样,额外的操作可以用用于对象列表或者单个实例。为了表明是否是单个实例,将detail参数设置为True
from rest_framework.decorators import action from rest_framework import status class BookListModelViewSet(ModelViewSet): queryset = BookInfo.objecets.all() serializer_class = BookInfoSerialzer #detail为True表示单个实例,网址为这种形式:^books/{pk}/set_bookname/$ @action(methods=['post'],detail=True) def set_bookname(self,request,pk=None): book = get_object() serializer = BookInfoSerialzer(data=request.data) if serializer.is_valid(): book.name = request.data.get('name') book.save() return Response({'message':'名字修改成功!'}) else: return Response(serializer.errors,status.HTTP_400_BAD_REQUEST) #detail设置为False表示列表,网址为这种形式:^books/order_comment/$ def order_comment(self,request): book = BookInfo.objects.all().order_by('commentcount') serializer = BookInfoSerialzer(instance=book,many=True) return Response(serializer.data)
method指明请求方式,detail用于控制是控制一组对象还是控制一个对象
路由设置不变化:
from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r'books',views.BookListModelViewSet) urlpatterns = [ url(r'^',include(router.urls)), ]
ReadOnlyModelViewSet
继承自GenericAPIView,同时包括了ListModelMixin,RetrieveModelMixin。
与ModelViewSet一样,也包括各种操作方法的实现,但不同于ModelViewSet的是,只提供”只读“操作,list()和retrieve()
路由Router
对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由列表信息。
REST framewok 提供了两个router
-
SimpleRouter
-
DefaultRouter
1.使用方法
1) 创建router对象,并注册视图集,注册语法为
register(prefix, viewset,base_name)
-
prefix 该视图集的路由前缀
-
viewset 视图集
-
base_name 路由名称的前缀
例如
from rest_framework import routers router = routers.SimpleRouter() router.register(r'books', BookViewSet, base_name='book')
如上述代码会形成的路由如下:
^books/$ name: book-list ^books/{pk}/$ name: book-detail
2)添加路由数据
可以有两种方式:
urlpatterns = [ ... ] urlpatterns += router.urls
或
urlpatterns = [ ... url(r'^', include(router.urls)) ]
DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。