1. Django 中的 View Class
首先回忆一下,Django 对请求的处理逻辑。收到一次请求之后,Django 会生成一个 WSGIHandler 类型的 handler,由 handler 控制整个处理流程。
那么,请求的 URL 与 View 是如何关联的呢?
Django 首先根据 ROOT_URLCONF
的配置加载 URLconf,按顺序逐个匹配 URLconf 的 URLpatterns,匹配即停止。 然后,将 HttpRequest 对象作为参数,调用 URLpatterns 的 view 函数。
再来看看,类视图路由的配置方法:
1
2
3
4
5
6
| from django.conf.urls import url
from myapp import views
urlpatterns = [
url(r'^fruit/', views.FruitView.as_view()),
]
|
django/views/generic/base.py
定义了 View 类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| class View(object):
@classonlymethod
def as_view(cls, **initkwargs):
...
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
...
return view
def dispatch(self, 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
return handler(request, *args, **kwargs)
|
通过调用 as_view()
方法,将返回 dispatch()
函数给路由函数。
dispatch()
函数,会根据请求的方法,在 View 类中调用与请求方法同名的函数。
这样,处理逻辑就非常清楚了。相较于函数视图,类视图多了 dispatch
这一步骤,可以理解为二次路由。
2. restframework 中的 View Class
restframwork 继承 Django View,实现了三个层级的 View,分别是 APIView、GenericAPIView、GenericViewSet。下面,我们来逐个分析。
2.1 APIView
rest_framewor/views.py
定义了 APIView 类:
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
| from rest_framework.request import Request
class APIView(View):
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
...
def initial(self, request, *args, **kwargs):
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
def dispatch(self, request, *args, **kwargs):
request = self.initialize_request(request, *args, **kwargs)
self.request = request
try:
self.initial(request, *args, **kwargs)
handler = getattr(self, request.method.lower(),
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
|
在 initial 函数中,会校验接口的版本、认证授权、权限验证、访问频率等。同时,restframework 对 request 进行了再次封装。
在使用上 restframework 的 APIView 与 Django View 差别不大,但是对 View 提供的功能进行了增强。
2.2 GenericAPIView
rest_framework/generics.py
定义了 GenericAPIView 类:
1
2
3
4
5
6
7
8
9
| class GenericAPIView(views.APIView):
queryset = None
serializer_class = None
lookup_field = 'pk'
lookup_url_kwarg = None
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
def get_queryset(self):
...
|
GenericAPIView 继承自 APIView,新增了一些类方法:
get_queryset
,获取 QuerySetget_object
,获取单条数据get_serializer
,获取序列化后的数据get_serializer_class
,获取需要序列化的model类get_serializer_context
,获取序列化的数据,定义了某种格式的字典paginator
,分页器
除了直接继承 GenericAPIView 实现 View Class,restframework 还提供了大量 mixins。通过继承 mixins,可以快速组合需要的操作。请看下面这个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| from rest_framework import mixins
from rest_framework import generics
class BookView(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializers
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
|
更多 mixins:
mixins | 功能 | 对应的 HTTP 请求方法 |
---|
mixins.ListModelMixin | 定义 list 方法,返回一个 querylist 的列表 | GET |
mixins.CreateModelMixin | 定义 create 方法,创建一个示例 | POST |
mixins.RetrieveModelMixin | 定义 retrieve 方法,返回一个具体的示例 | GET |
mixins.UpdateModelMixin | 定义 update 方法,对某个实例进行更新 | PUT/PATCH |
mixins.DestroyModelMixin | 定义 delete 方法,删除某个实例 | DELETE |
2.4 GenericViewSet
GenericAPIView 提供了增、删、改、查等基本操作,可以用于快速开发 API。但,如果需要对这些操作进行组合,实现复杂的业务逻辑时,可能就不那么方便了。
幸运的是,restframework 提供了 GenericViewSet,用于更复杂的业务逻辑处理。
rest_framework/viewsets.py 中定义了 GenericAPIView 类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class ViewSetMixin(object):
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
return self.dispatch(request, *args, **kwargs)
view.cls = cls
view.initkwargs = initkwargs
view.suffix = initkwargs.get('suffix', None)
view.actions = actions
return csrf_exempt(view)
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
pass
|
内置的 ViewSet 有 5 种,分别是
- ViewSetMixin
- ViewSet
- GenericViewSet
- ReadOnlyModelViewSet
- ModelViewSet
有两种方式,可以将 ViewSet 绑定到指定的 URL 上:
- 手动将 ViewSet 绑定到 URL
在 urls.py 中,新增:
1
2
3
4
5
6
7
8
9
| from .views import SnippetViewSet
snippet_list = SnippetViewSet.as_view({
'get': 'list',
'post': 'create'
})
urlpatterns = [
url(r'^snippets/$', snippet_list, name='snippet-list'),
|
将 snippets/
路径下,get 请求方法,分发到 list
函数;post
请求方法,分发到 create
方法。
- 使用 restframework 提供的 routers
在 urls.py 中,新增:
1
2
3
4
5
6
7
8
9
| from rest_framework.routers import DefaultRouter
from .views import SnippetViewSet
router = DefaultRouter()
router.register(r'snippets', SnippetViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
]
|
3. Serializer
restframework 中的 Serializer 和 ModelSerializer 与 Django 的 Form 和 ModelForm 类似。
3.1 Serializer
1
2
3
4
5
| from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
|
1
2
3
| serializer = CommentSerializer(comment)
serializer.data
{'email': '[email protected]', 'content': 'foo bar'}
|
1
2
3
4
5
| serializer = CommentSerializer(data=data)
serializer.is_valid()
True
serializer.validated_data
{'content': 'foo bar', 'email': '[email protected]'
|
还可以用于验证,提交的数据是否符合合法:
1
2
3
| serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
pass
|
3.2 ModelSerializer
相较与上面一个字段一个字段的声明,ModelSerializer 可以快速建立相关模型的 serializer 模型。提供如下特性:
- 自动产生基于模型的 fileds
- 自动产生验证器,比如
unique_together
验证器 - 默认包含 create 和 update 方法
- 外键被映射为
PrimaryKeyRelatedField
1
2
3
4
5
6
| from .models import Snippet
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
|
4. 参考