Jiang's blog

DRF源码解析2--viewsets.ModelViewSet对视图的封装

Word count: 745Reading time: 3 min
2020/02/19 Share

viewsets

drf中 viewsets 对 view 进行了更加深层的封装,在CBV编程中减少代码的冗余,截取viewsets源码如下

1
2
3
4
5
6
7
8
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
# 可以看到viewsets中的ModelViewSet只是继承了各种类,每一个类对应CBV的每一个方法,如CreateModelMixin则对应post方法
pass

进入GenericViewSet的源码中,不难发现,GenericViewSet这个类主要是获取我们的数据和处理数据的组件

1
2
3
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
# GenericViewSet同样是继承了两个类
pass

进入generics.GenericAPIView

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
class GenericAPIView(views.APIView):
# queryset就是获取的ORM对象
queryset = None
# serializer_class是序列化的类
serializer_class = None

lookup_field = 'pk'
lookup_url_kwarg = None

# The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

# The style to use for queryset pagination.
# pagination_class就是和分页组件相关的类,默认为drf配置中的组件
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

def get_queryset(self):
# 这里断言就是为了说明在继承这个类时,一定要有queryset属性或覆盖这个方法
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
# 获取ORM对象
queryset = self.queryset
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all()
return queryset

def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
# serializer_class接收的参数可能不同
return serializer_class(*args, **kwargs)

def get_serializer_class(self):
# 继承这个类时,一定要有serializer_classt属性或覆盖这个方法
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)

return self.serializer_class

def get_serializer_context(self):
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}

def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset

接着回到ModelViewSet中,进入到mixins.CreateModelMixin的源码中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
# 这里调用的就是GenericAPIView中的get_serializer
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 {}

现在我们只需要继承ModelViewSet,然后重写queryset和处理数据所需要的组件类如序列化的类等,但是怎么将self.post对应上self.list方法呢,GenericViewSet还继承了ViewSetMixin,看一下ViewSetMixin的源码的截取

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
class ViewSetMixin:
@classonlymethod
# 对as_view()方法又一次封装,action表示可以接收参数了
def as_view(cls, actions=None, **initkwargs):
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")

def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions

# Bind methods to actions
# This is the bit that's different to a standard view
# method, action退出action为一个字典,{'get':'list'}
for method, action in actions.items():
# handler为实例中的各个方法
handler = getattr(self, action)
# 这里setattr相当于metmod == handler
setattr(self, method, handler)

if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get

self.request = request
self.args = args
self.kwargs = kwargs

# And continue as usual
return self.dispatch(request, *args, **kwargs)
return csrf_exempt(view)

奉献一张图来看下我们的继承顺序

viewsets.png

CATALOG
  1. 1. viewsets
    1. 1.0.1. 奉献一张图来看下我们的继承顺序