OverView
Django View -- Root
https://docs.djangoproject.com/en/3.2/ref/class-based-views/base/#view
所有view视图的基类, 此类不是通用视图,被业务视图继承。
- class
django.views.generic.base.
View
¶The master class-based base view. All other class-based views inherit from this base class. It isn’t strictly a generic view and thus can also be imported from
django.views
.
主要功能是做映射: http method --> view method
HTTP METHOD --> VIEW.METHOD
GET --> View.get
POST --> View.post
PUT --> View.put
DELETE --> View.delete
视图业务定义在 http method名称的方法中。
from django.http import HttpResponse from django.views import View class MyView(View): def get(self, request, *args, **kwargs): return HttpResponse('Hello, World!')
使用as_view方法,将视图类转换为 callable 视图。
from django.urls import path from myapp.views import MyView urlpatterns = [ path('mine/', MyView.as_view(), name='my-view'), ]
DRF APIView
--> Django View
https://www.django-rest-framework.org/api-guide/views/
继承了Dangjo View,
同Django View相同点:
有 HTTP METHOD 到 VIEW METHOD的映射
业务方法定义在 VIEW METHOD 方法中
不同的地方:
新增了一些控制属性
Using the
APIView
class is pretty much the same as using a regularView
class, as usual, the incoming request is dispatched to an appropriate handler method such as.get()
or.post()
. Additionally, a number of attributes may be set on the class that control various aspects of the API policy.
DRF 控制属性
https://testdriven.io/blog/drf-views-part-1/
Attribute Usage Examples renderer_classes
determines which media types the response returns JSONRenderer
,BrowsableAPIRenderer
parser_classes
determines which data parsers for different media types are allowed JSONParser
,FileUploadParser
authentication_classes
determines which authentication schemas are allowed for identifying the user TokenAuthentication
,SessionAuthentication
throttle_classes
determines if a request should be authorized based on the rate of requests AnonRateThrottle
,UserRateThrottle
permission_classes
determines if a request should be authorized based on user credentials IsAuthenticated
,DjangoModelPermissions
content_negotiation_class
selects one of the multiple possible representations of the resource to return to a client (unlikely you'll want to set it up) only custom content negotiation classe
在view.method 之外增加 控制属性
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import authentication, permissions from django.contrib.auth.models import User class ListUsers(APIView): """ View to list all users in the system. * Requires token authentication. * Only admin users are able to access this view. """ authentication_classes = [authentication.TokenAuthentication] permission_classes = [permissions.IsAdminUser] def get(self, request, format=None): """ Return a list of all users. """ usernames = [user.username for user in User.objects.all()] return Response(usernames)
DRF GenericAPIView --> DRF APIView
https://www.django-rest-framework.org/api-guide/generic-views/#genericapiview
DRF APIView 相比 Django View, 增加了一些控制属性, 例如对接口的访问权限和限流进行控制。
DRF GenericAPIView 相比 DRF APIView,又增加了一些控制属性, 这些控制属性,是专门面向典型的多实例数据(数据库表)设计,
让用户简单定义一个API, 可以完成对应一个数据库表的CRUD操作。
This class extends REST framework's
APIView
class, adding commonly required behavior for standard list and detail views.
如下三个:
queryset --> 使用Model定义查询集合
serializer_class --> 定义模型的序列化类
pagination_class --> 分页控制
queryset
- The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override theget_queryset()
method. If you are overriding a view method, it is important that you callget_queryset()
instead of accessing this property directly, asqueryset
will get evaluated once, and those results will be cached for all subsequent requests.serializer_class
- The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override theget_serializer_class()
method.pagination_class
- The pagination class that should be used when paginating list results. Defaults to the same value as theDEFAULT_PAGINATION_CLASS
setting, which is'rest_framework.pagination.PageNumberPagination'
. Settingpagination_class=None
will disable pagination on this view.
https://testdriven.io/blog/drf-views-part-2/#genericapiview
基于新增的控制属性, 可以非常方便地实现 GET 和 DELETE 业务。
from rest_framework.generics import GenericAPIView from rest_framework.response import Response class RetrieveDeleteItem(GenericAPIView): serializer_class = ItemSerializer queryset = Item.objects.all() def get(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data) def delete(self, request, *args, **kwargs): instance = self.get_object() instance.delete() return Response(status=status.HTTP_204_NO_CONTENT)
DRF Concrete View Classes --> DRF GenericAPIView
DRF GenericAPIView 是从数据层面上定义了一些公共属性, 这些属性服务于单个数据库表,
使得在 get delete方法中,可以方便地操作数据, 和 序列化数据, 见上节GenericAPIView示例代码。
如果后台逻辑非常简单,仅仅局限在数据库表的 CRUD操作, 那么get delete方法中的代码就存在公用性,
更好的方法是将这些公共的代码,封装起来(Mixins),使用的时候继承使用。
Mixins
在各个Mixin中封装好 view.method中的公用代码。
https://www.django-rest-framework.org/api-guide/generic-views/#mixins
The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as
.get()
and.post()
, directly. This allows for more flexible composition of behavior.The mixin classes can be imported from
rest_framework.mixins
.
所有的Mixin都是不能单独使用的, 需要个GenericAPIView搭配使用。
但是这些Mixin仅仅做公共代码逻辑的封装,并不提供view.method的实现。
https://testdriven.io/blog/drf-views-part-2/#mixins
Mixins provide bits of common behavior. They cannot be used standalone; they must be paired with
GenericAPIView
to make a functional view. While the mixin classes provide create/retrieve/update/delete actions, you still need to bind the appropriate actions to the methods.
Mixin Usage CreateModelMixin
Create a model instance ListModelMixin
List a queryset RetrieveModelMixin
Retrieve a model instance UpdateModelMixin
Update a model instance DestroyModelMixin
Delete a model instance
例如 RetrieveModelMixin 仅仅实现 retrieve 方法。
class RetrieveModelMixin: """ Retrieve a model instance. """ def retrieve(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data)
基于此, 可以使得 view.method更加简洁, 例如。
实际上,如无特殊逻辑(数据库表操作之外的逻辑), 考虑到通用性, get 和 post的方法, 也不应该让用户维护。
from rest_framework import mixins from rest_framework.generics import GenericAPIView class CreateListItems(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView): serializer_class = ItemSerializer queryset = Item.objects.all() def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
Concrete View Classes
具体的视图类,就是为了考虑的数据库表操作的通用性, 竭尽所能,让用户少维护代码。
https://www.django-rest-framework.org/api-guide/generic-views/#concrete-view-classes
The following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior.
The view classes can be imported from
rest_framework.generics
.
例如ListCreateAPIView, 实现了 Mixin.method 绑定到 View.method中。
https://testdriven.io/blog/drf-views-part-2/#concrete-views
# https://github.com/encode/django-rest-framework/blob/3.12.4/rest_framework/generics.py#L232 class ListCreateAPIView(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView): def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
具体使用上,就变成了声明式, 无需关心 get 和 post方法实现:
from rest_framework.generics import ListCreateAPIView class ListCreateItems(ListCreateAPIView): authentication_classes = [TokenAuthentication] queryset = Item.objects.all() serializer_class = ItemSerializer
具体视图类包括以下若干种:
https://testdriven.io/blog/drf-views-part-2/#concrete-views
Class Usage Method handler Extends mixin CreateAPIView
create-only post
CreateModelMixin
ListAPIView
read-only for multiple instances get
ListModelMixin
RetrieveAPIView
read-only for single instance get
RetrieveModelMixin
DestroyAPIView
delete-only for single instance delete
DestroyModelMixin
UpdateAPIView
update-only for single instance put
,patch
UpdateModelMixin
ListCreateAPIView
read-write for multiple instances get
,post
CreateModelMixin
,ListModelMixin
RetrieveUpdateAPIView
read-update for single instance get
,put
,patch
RetrieveModelMixin
,UpdateModelMixin
RetrieveDestroyAPIView
read-delete for single instance get
,delete
RetrieveModelMixin
,DestroyModelMixin
RetrieveUpdateDestroyAPIView
read-update-delete for single instance get
,put
,patch
,delete
RetrieveModelMixin
,UpdateModelMixin
,DestroyModelMixin
ViewSet --> APIView, ViewSetMixin
ViewSetMixin
ViewSet 继承于 APIView,
拥有两个能力:
(1)HTTP.METHOD 到 VIEW.METHOD 映射能力 (django view)
(2)定义接口访问控制属性 (DRF APIView)
其还继承了 ViewSetMixin
:
此类重写了 as_view 接口, 将HTTP.METHOD映射到MIXIN.ACTION. 这样 业务开发者只需要关注ACTION 接口实现。
ViewSetMixin
https://testdriven.io/blog/drf-views-part-3/#viewset-class
ViewSetMixin
is a class where all the "magic happens". It's the only class that all four of the ViewSets share. It overrides theas_view
method and combines the method with the proper action.
Method List / Detail Action post
List create
get
List list
get
Detail retrieve
put
Detail update
patch
Detail partial_update
delete
Detail destroy
ViewSet
继承ViewSet的子类(业务类)需要实现 VIEW.ACTION.
https://www.django-rest-framework.org/api-guide/viewsets/#viewset
The
ViewSet
class inherits fromAPIView
. You can use any of the standard attributes such aspermission_classes
,authentication_classes
in order to control the API policy on the viewset.The
ViewSet
class does not provide any implementations of actions. In order to use aViewSet
class you'll override the class and define the action implementations explicitly.
例如, 业务action被实现,
但是此类是继承于APIView,将 HTTP.METHOD映射到 VIEW.METHOD
并没有将HTTP.METHOD映射到 MIXIN.ACTION
from django.shortcuts import get_object_or_404 from rest_framework.response import Response from rest_framework.viewsets import ViewSet class ItemViewSet(ViewSet): queryset = Item.objects.all() def list(self, request): serializer = ItemSerializer(self.queryset, many=True) return Response(serializer.data) def retrieve(self, request, pk=None): item = get_object_or_404(self.queryset, pk=pk) serializer = ItemSerializer(item) return Response(serializer.data)
Handling URLs
ViewSet注册URL方式,使用 两种类型的 router。
https://testdriven.io/blog/drf-views-part-3/#handling-urls
Instead of using Django's urlpatterns, ViewSets come with a router class that automatically generates the URL configurations.
DRF comes with two routers out-of-the-box:
The main difference between them is that DefaultRouter includes a default API root view:
The default API root view lists hyperlinked list views, which makes navigating through your application easier.
例如:
# urls.py from django.urls import path, include from rest_framework import routers from .views import ChangeUserInfo, ItemsViewSet router = routers.DefaultRouter() router.register(r'custom-viewset', ItemsViewSet) urlpatterns = [ path('change-user-info', ChangeUserInfo.as_view()), path('', include(router.urls)), ]
GenericViewSet --> GenericAPIView, ViewSetMixin
, ViewSetMixin
GenericAPIView
继承于 APIView,
同时设计了 面向数据表的 控制属性,
GenericViewSet 继承了
GenericAPIView
,
同时也继承了ViewSetMixin,将HTTP.METHOD映射到MIXIN.ACTION,子类(业务类)则需要实现ACTION。
https://www.django-rest-framework.org/api-guide/viewsets/#genericviewset
The
GenericViewSet
class inherits fromGenericAPIView
, and provides the default set ofget_object
,get_queryset
methods and other generic view base behavior, but does not include any actions by default.In order to use a
GenericViewSet
class you'll override the class and either mixin the required mixin classes, or define the action implementations explicitly.
显示实现ACTION
例如 list create ...
https://testdriven.io/blog/drf-views-part-3/#genericviewset
from rest_framework import status from rest_framework.permissions import DjangoObjectPermissions from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet class ItemViewSet(GenericViewSet): serializer_class = ItemSerializer queryset = Item.objects.all() permission_classes = [DjangoObjectPermissions] def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save(serializer) return Response(serializer.data, status=status.HTTP_201_CREATED) def list(self, request): serializer = self.get_serializer(self.get_queryset(), many=True) return self.get_paginated_response(self.paginate_queryset(serializer.data)) def retrieve(self, request, pk): item = self.get_object() serializer = self.get_serializer(item) return Response(serializer.data) def destroy(self, request): item = self.get_object() item.delete() return Response(status=status.HTTP_204_NO_CONTENT)
直接继承MIXINS
将mixins中实现过的 MIXINS 继承过来。
https://testdriven.io/blog/drf-views-part-3/#genericviewset
from rest_framework import mixins, viewsets class ItemViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): serializer_class = ItemSerializer queryset = Item.objects.all()
ModelViewSet --> GenericViewSet, Mixins
, Mixins
GenericViewSet 的缺点是, 需要自行实现 action,
对于通用性(只需要实现数据库表操作)的场景, action都是一样的,
此类将所有的 Mixins类继承过来, 则所有的CRUD操作都已经支持。
https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset
The
ModelViewSet
class inherits fromGenericAPIView
and includes implementations for various actions, by mixing in the behavior of the various mixin classes.The actions provided by the
ModelViewSet
class are.list()
,.retrieve()
,.create()
,.update()
,.partial_update()
, and.destroy()
.
对于一个数据库表, 只需要做如下声明, 即支持了所有的CRUD
class ItemModelViewSet(ModelViewSet): serializer_class = ItemSerializer queryset = Item.objects.all()
路由注册到URL
# urls.py from django.urls import path, include from rest_framework import routers from .views import ChangeUserInfo, ItemsViewSet, ItemModelViewSet router = routers.DefaultRouter() router.register(r'custom-viewset', ItemsViewSet) router.register(r'model-viewset', ItemModelViewSet) # newly registered ViewSet urlpatterns = [ path('change-user-info', ChangeUserInfo.as_view()), path('', include(router.urls)), ]
ReadOnlyModelViewSet --> GenericViewSet, Mixins(list retrieve)
, Mixins(list retrieve)
只提供读取的 ACTION.
https://www.django-rest-framework.org/api-guide/viewsets/#readonlymodelviewset
The
ReadOnlyModelViewSet
class also inherits fromGenericAPIView
. As withModelViewSet
it also includes implementations for various actions, but unlikeModelViewSet
only provides the 'read-only' actions,.list()
and.retrieve()
.
非写接口。
from rest_framework.viewsets import ReadOnlyModelViewSet class ItemReadOnlyViewSet(ReadOnlyModelViewSet): serializer_class = ItemSerializer queryset = Item.objects.all()
https://img2020.cnblogs.com/blog/603834/202110/603834-20211012235850083-113696365.png