Restframework APIView源码解读

前言

Djangorestframework的视图主要有3种写法,分别为APIView(@api_view)、GenericAPIView、viewsets。

APIView

一般用法

1
2
3
4
5
6
7
8
9
10
11
class ListUsers(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permission.IsAdminUser,)

def get(self, request, format=None):
return Response()

@api_view(['GET', 'POST'])
@permission_classes([AllowAny, ])
def ListUser(request):
return Response()

部分源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class APIView(View): # APIView继承自Django的view

# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES #解析
parser_classes = api_settings.DEFAULT_PARSER_CLASSES #渲染处理
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES #认证
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES #节流处理,控制api访问次数
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES #权限
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS #内容协商
metadata_class = api_settings.DEFAULT_METADATA_CLASS #元
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS #版本管理

@classmethod # 可以移除CSRF
def as_view(cls, **initkwargs):
"""
Store the original class on the view function.

This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation.
"""
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
cls.queryset._result_iter = force_evaluation # Django <= 1.5

view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls

# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
return csrf_exempt(view)

... # 很多属性和方法

# dispatch能让get, post, put, patch, delete方法直接调用get等request method
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs) # 初始化
self.request = request
self.headers = self.default_response_headers # deprecate?

try:
self.initial(request, *args, **kwargs)

# Get the appropriate handler method
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

response = handler(request, *args, **kwargs) #

except Exception as exc:
response = self.handle_exception(exc) # 异常处理

self.response = self.finalize_response(request, response, *args, **kwargs) # 响应结束钩子
return self.response

Generic views

Generic views继承自APIView,提供额外的属性和方法,如queryset, seializer_class, lookup_field, lookup_url_kwarg, pagination_class, 提供的方法有get_queryst(), get_object(), get_seializer(), filter_queryset()等,一般不单用,和mixins组合成其他的视图类。
mixin类:

  • CreateModelMixin 提供create(), perform_create() 相当于post,提供简单的覆盖保存
  • ListModelMixin 提供list(), 相当于get列表
  • RetrieveModelMixin retrieve(), 相当于get一个对象
  • UpdataModelMixin update(), perform_update(), partial_update()
  • DestroyModelMixin destroy(), perform_destroy()

GenericAPIView +
CreateModelMixin = CreateAPIView 提供post()
ListModelMixin = ListAPIView get()
R = R get()
U = U put(), patch()
D = D delete()
其他:ListCreateAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView

viewsets

ViewSet继承APIView+ViewSetMixin(提供as_view()和initialze_request()方法
ReadOnlyModelViewSet = mixins.RetrieveModelMixin + mixins.ListModelMixin + GenericaViewSet 只提供list()和retrieve()
ModelViewSet 都提供: list(), create(), retrieve(), update(), partial_update(), destroy()
提供额外的路由

1
2
3
@action(methods=['post', 'delete'], detail=True, permission_classes=[IsAdminUser])
def set_password(self, request, pk=None):
pass

----------本文完,感谢您的阅读----------