drf的视图组件
到这里,已经学习了REST framework的序列化、请求、响应.
远不于此, REST framework还提供了众多的通用视图基类与扩展类,以简化视图的编写!!
简单回顾下前面学的.
大体的流程弄清楚了,其余的都好办.
Django CBV的View类的源码.
IndexView.as_view()、views.test -- 都是指向一个函数地址
最主要是dispatch做了一个分发.根据request.method()来通过反射调用CBV里的get、post方法.
drf APIView的源码.
在View的基础上,还做了三件事.
1> 旧的request封装成新的 实现细节涉及到Request类
- 旧的request 通过request._request可以访问到
- 新的request用起来跟旧的一样,因为新的重写了 __getattr__ 方法
- POST请求提交的数据 都被 request.data接收
json数据 - dict; urlencoded、formdata - queryset对象. 简单当作字典使用即可.
2> 在执行视图类的方法之前,执行了三大认证
3> 捕获全局异常,包括三大认证和视图类中的方法
# -----------------
"""
ser = BookSerializer(instance=book, data=request.data, many=False)
if ser.is_valid():
ser.save()
"""
1> ser.is_valid() 反序列化的校验有顺序
for 字段 in 字段们:
字段自己的校验 - 字段局部钩子的校验
最后全局钩子的校验
2> ser.save()
会根据ser的instance属性是否为None,来看用create还是update!
3> many参数的值也会决定ser的什么对象.
many=True -- ListSerializer对象,相当于一个列表,里面套了一个个BookSerializer的实例对象!
many=False -- BookSerializer的实例对象
序列化类Serializer和ModelSerializer的具体细节就不赘述啦.看ModelSerializer章节里的总结.
# -----------------
请求与响应
请求就request.data
视图类里的方法最后一般都返回的是 return Response(request.data) 就看这个Response()你想如何进行初始化.
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
# 第一层 APIView
第一层: 继承APIView完全自己写 (つД`)ノ
思考一个问题, 我们写了Book的5个接口, 若还要写Publish的5个接口,只需要改动代码中一些类名便可实现.
代码太冗余了.纯纯的大冤种.我们能想到的, drf的开发者一定也能想到!!
# APIView & View
用稍微官方的语言再简单回顾下APIView与View的不同之处:
1> 传入到视图方法中的是REST framework的Request
对象, 而不是Django的HttpRequeset
对象;
2> 在进行dispatch()分发前, 会对请求进行身份认证、权限检查、流量控制;
3> 任何APIException
异常都会被捕获到, 并且处理成合适的响应信息;
4> 视图方法可以返回REST framework的Response
对象, 视图会为响应数据设置(render)符合前端要求的格式;
# 五个接口复现
models.py
记得进行数据库迁移的两行命令!
from django.db import models
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField()
# -- 若to=Publish, Publish不加引号,那么Publish类要写到Book类前面才找得到
publish = models.ForeignKey(to=Publish, on_delete=models.CASCADE)
def publish_detail(self):
return {"name": self.publish.name, "addr": self.publish.addr}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
serializer.py
from rest_framework import serializers
from .models import Book, Publish
class BookSerializer(serializers.ModelSerializer):
publish_detail = serializers.DictField(read_only=True)
class Meta:
model = Book
fields = ['name', 'price', 'publish', 'publish_detail']
extra_kwargs = {
'publish': {'write_only': True}
}
class PublishSerializer(serializers.ModelSerializer):
class Meta:
model = Publish
fields = '__all__'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>', views.BookDetailView.as_view()),
]
2
3
4
5
6
7
8
9
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from . import serializer
from .models import Book, Publish
class BookView(APIView):
def get(self, request):
qs = Book.objects.all()
ser = serializer.BookSerializer(instance=qs, many=True)
return Response(data=ser.data, status=200)
def post(self, request):
ser = serializer.BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(data={'code': 100, 'msg': '创建成功!'})
else:
return Response(data=ser.errors)
class BookDetailView(APIView):
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = serializer.BookSerializer(instance=book)
return Response(data=ser.data, status=200)
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = serializer.BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save()
return Response(data={'code': 100, 'msg': '修改成功!'})
else:
return Response(data=ser.errors)
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
# -- 若看到有的直接return {'code': 100, 'msg': '删除成功!'},那就是做了个封装,写了个装饰器啥的.
return Response(data={'code': 100, 'msg': '删除成功!'})
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
注意: 添加一本书
POST请求 http://127.0.0.1:8000/books/
往后端提交json数据 {"name": "红楼梦","price": 19, "publish": 1}
后端返回前端 {"publish":["Invalid pk "1" - object does not exist."]}
因为id为1的出版社不存在, 所以为了实验(没有写出版社的五个接口), 需要先手动添加一条出版社的数据!!
查询所有书籍
GET请求 http://127.0.0.1:8000/books/
后端返回前端 [{"name":"红楼梦","price":19,"publish_detail":{"name":"上海出版社","addr":"上海"}}]
其余接口同理.不再测试.
# 第二层 GenericAPIView
GenericAPIView 通用视图类, 继承自
APIVIew
两个类属性 : 都有关数据库的操作.
两个类属性 | 含义 |
---|---|
queryset | queryset = Book.objects.all() -- 想要序列化的数据 |
serializer_class | serializer_class = serializer.BookSerializer -- 使用的序列化类 |
三个方法
三个方法 | 含义 |
---|---|
get_queryset() | 获取要序列化的数据 qs=self.get_queryset() |
get_object() | 获取单条数据 book=self.get_object() self.get_object().delete() -- delete |
get_serializer() | 获取要序列化的类 self.get_serializer(instance=qs, many=True) -- get self.get_serializer(data=request.data) -- post self.get_serializer(instance=book) -- get self.get_serializer(data=request.data, instance=book) -- put |
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from . import serializer
from .models import Book, Publish
class BookView(GenericAPIView):
queryset = Book.objects.all() # -- 你要序列化的数据
serializer_class = serializer.BookSerializer # -- 你要使用的序列化类
def get(self, request):
# qs = Book.objects.all()
# qs = self.queryset # -- 可以但不建议这样写
# -- 查看源码得知,get_queryset()里执行了语句
# queryset = self.queryset return queryset
qs = self.get_queryset()
# ser = serializer.BookSerializer(instance=qs, many=True)
# ser = self.serializer_class(instance=qs, many=True) # -- 可以但不建议这样写
# -- 可查看get_serializer源码,别疑惑为啥要兜一圈,感觉复杂化啦.
# 你要知道drf的作者一定比你强.这样操作,可以重写get_serializer_class方法来使用不同的序列化类.
# 这使得5个接口不一定都使用同一个序列化类.Hhh.
ser = self.get_serializer(instance=qs, many=True)
return Response(data=ser.data, status=200)
def post(self, request):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(data={'code': 100, 'msg': '创建成功!'})
else:
return Response(data=ser.errors)
class BookDetailView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = serializer.BookSerializer
# def get(self, request, pk):
def get(self, request, *args, **kwargs):
# book = Book.objects.filter(pk=pk).first()
# -- GenericAPIView类里有个类变量 lookup_field = 'pk'
# path转换器<转换器类型:变量名>里的变量名要为'pk'
# 若在BookDetailView类里重写lookup_field变量,转换器里的变量要跟着改!
book = self.get_object() # -- 获取单条!!
# ser = serializer.BookSerializer(instance=book)
ser = self.get_serializer(instance=book)
return Response(data=ser.data, status=200)
def put(self, request, pk):
# book = Book.objects.filter(pk=pk).first()
book = self.get_object()
# ser = serializer.BookSerializer(data=request.data, instance=book)
ser = self.get_serializer(data=request.data, instance=book)
if ser.is_valid():
ser.save()
return Response(data={'code': 100, 'msg': '修改成功!'})
else:
return Response(data=ser.errors)
def delete(self, request, pk):
# Book.objects.filter(pk=pk).delete()
self.get_object().delete()
return Response(data={'code': 100, 'msg': '删除成功!'})
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
# 第三层 5个视图扩展类
继承GenericAPIView + 5个视图扩展类 两者需一起使用!5个视图扩展类不能单独用.
为何称为视图扩展类呢? 因为这5个类不是视图类,它们没有继承APIView及其子类!
扩展类 | 方法 |
---|---|
ListModelMixin | list() -- 获取所有表记录 |
CreateModelMixin | create() -- 创建一条表记录 |
RetrieveModelMixin | retrieve() -- 获取一条表记录 |
UpdateModelMixin | update() -- 修改一条表记录 |
DestroyModelMixin | destroy() -- 删除一条表记录 |
# 精简版
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, \
RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
from . import serializer
from .models import Book, Publish
class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Book.objects.all()
serializer_class = serializer.BookSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Book.objects.all()
serializer_class = serializer.BookSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
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
# 源码讲解版
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, \
RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
from . import serializer
from .models import Book, Publish
class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Book.objects.all()
serializer_class = serializer.BookSerializer
def get(self, request):
# qs = self.get_queryset()
# ser = self.get_serializer(instance=qs, many=True)
# return Response(data=ser.data, status=200)
# -- list()方法是ListModelMixin提供的!它干了什么事呢?
# 查看源码.上面注释的代码都做了.
"""
class ListModelMixin:
def list(self, request, *args, **kwargs):
# -- 加了个过滤,拿到self.get_queryset()
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)
"""
return self.list(request)
def post(self, request):
# ser = self.get_serializer(data=request.data)
# if ser.is_valid():
# ser.save()
# return Response(data={'code': 100, 'msg': '创建成功!'})
# else:
# return Response(data=ser.errors)
# -- create()方法是CreateModelMixin提供的!它干了什么事呢?
# 查看源码.上面注释的代码都做了.
"""
class CreateModelMixin:
def create(self, request, *args, **kwargs):
# -- 获得序列化的类对象
serializer = self.get_serializer(data=request.data)
# -- 这种校验写法 比我们通过if serializer.is_valid():.. 的方式更简短
serializer.is_valid(raise_exception=True)
# -- 就是执行了serializer.save().那为啥还有封装成一个方法呢?
# 这样我们就可以在BookView类里重写perform_create方法,加一些校验.
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 {}
"""
return self.create(request)
class BookDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Book.objects.all()
serializer_class = serializer.BookSerializer
def get(self, request, *args, **kwargs):
# book = self.get_object()
# ser = self.get_serializer(instance=book)
# return Response(data=ser.data, status=200)
# -- retrieve()方法是RetrieveModelMixin提供的!它干了什么事呢?
# 查看源码:
"""
class RetrieveModelMixin:
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
"""
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
# book = self.get_object()
# ser = self.get_serializer(data=request.data, instance=book)
# if ser.is_valid():
# ser.save()
# return Response(data={'code': 100, 'msg': '修改成功!'})
# else:
# return Response(data=ser.errors)
# -- update()方法是RetrieveModelMixin提供的!它干了什么事呢?
# 查看源码: 稍微复杂一点,但本质都差不多.
"""
class UpdateModelMixin:
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)
# -- . perform_update方法里就是一行语句 serializer.save()
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
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)
"""
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
# self.get_object().delete()
# return Response(data={'code': 100, 'msg': '删除成功!'})
# -- destroy()方法是DestroyModelMixin提供的!它干了什么事呢?
# 查看源码:
"""
class DestroyModelMixin:
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
# -- 可以重写perform_destroy,实现一些需求. eg:在删除一个用户之前,记录一条日志.
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
"""
return self.destroy(request, *args, **kwargs)
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# 第四层 9个视图子类
内心OS: 封装的有点小离谱.. drf的开发者有点东西的啊.
重写 | GenericAPIView | ListModel Mixin | CreateModel Mixin | RetrieveModel Mixin | UpdateModel Mixin | DestroyModel Mixin | |
---|---|---|---|---|---|---|---|
get - list() | post - create() | get - retrieve() | put - update() | delete - destroy() | |||
LIST APIView | get | ☑️ | ☑️ | ||||
Create APIView | post | ☑️ | ☑️ | ||||
List Create APIView | get post | ☑️ | ☑️ | ☑️ | |||
--- | --- | --- | --- | --- | --- | --- | --- |
Retrieve APIView | get | ☑️ | ☑️ | ||||
Update APIView | put | ☑️ | ☑️ | ||||
Destroy APIView | delete | ☑️ | ☑️ | ||||
Retrieve Update APIView | get put | ☑️ | ☑️ | ☑️ | |||
Retrieve Destroy APIView | get delete | ☑️ | ☑️ | ☑️ | |||
Retrieve Update Destroy APIView | get put delete | ☑️ | ☑️ | ☑️ | ☑️ |
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, \
RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView
from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView
from rest_framework.generics import RetrieveUpdateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView
from . import serializer
from .models import Book, Publish
class BookView(ListCreateAPIView):
queryset = Book.objects.all()
serializer_class = serializer.BookSerializer
class BookDetailView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = serializer.BookSerializer
"""ListAPIView的源码,其余的同理!
class ListAPIView(mixins.ListModelMixin,GenericAPIView):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
"""
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
# 第五层 ModelViewSet
ModelViewSet 视图集
你可以很轻松的发现用9个视图子类实现的代码, BookView类和BookDetailView类里的代码是一样的.. 还可以更简洁.
用一个类就行, 但里面会有两个get, 一个获取所有,一个获取单条, 所以 得改一改路由的配置!!
# 代码实现
# views.py
from rest_framework.viewsets import ModelViewSet
from . import serializer
from .models import Book
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = serializer.BookSerializer
2
3
4
5
6
7
# urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# path('books/', views.BookView.as_view()),
# path('books/<int:pk>', views.BookDetailView.as_view()),
# -- 继承ModelViewSet后的路由写法
# 向地址 books/ 发get请求,会触发BookView视图类的list方法的执行
# 该list方法在视图类BookView继承的父类ModelViewSet里是提前封装好了的!! 其余同理.
path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ViewSetMixin类
ViewSetMixin类的源码分析
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
ModelViewSet类继承了GenericViewSet. GenericViewSet又是啥呢?
查看源码得知,GenericViewSet不仅继承了GenericAPIView,还继承了ViewSetMixin!
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
pass
那么问题来了,ViewSetMixin类是什么?
from rest_framework.viewsets import ViewSetMixin
★ 查看源码,发现ViewSetMixin类重写了as_view方法!!
所以路由的写法,触发的是ViewSetMixin的as_view!!
想一想,也很容易理解,无论是APIView还是Django里的View都不允许在as_view()里传入一个字典映射关系吧!
path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
ViewSetMixin的as_view的源码如下:
请求来了,路由匹配成功.会执行as_view内部的view -- view(request)
@classonlymethod
# -- actions就是我们传进去的那个字典,不传的话,直接报错
# 意味着,只要继承了ViewSetMixin及其子类,就得传actions
# -- 这里cls是BookView,因为是BookView这个类调用的as_view方法
# 只不过这个方法在BookView - ModelViewSet - ViewSetMixin里罢了
# 即调用的是BookView父类的父类里的as_view方法
def as_view(cls, actions=None, **initkwargs):
... ...
def view(request, *args, **kwargs):
# -- BookView视图类实例化得到了一个实例对象self
self = cls(**initkwargs)
... ...
# -- 假设actions = {'get': 'list', 'post': 'create'}
for method, action in actions.items():
# -- self是视图类BookView的实例化对象
# -- 通过反射去找该实例里的list方法
# 在BookView的父类ModelViewSet类继承的5个视图扩展类中能找到!!
handler = getattr(self, action)
# -- 通过反射往self的命名空间里写入 self.method = handler
# 即self.get = list
# 所以一旦在dispatch里get方法匹配成功,实际上执行的是list方法!!其余的post、put、delete同理.
# -- 简单来说就是动态的将 get替换成list ..
setattr(self, method, handler)
self.request = request
self.args = args
self.kwargs = kwargs
# And continue as usual
return self.dispatch(request, *args, **kwargs)
... ...
return csrf_exempt(view)
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
# 视图集下其他类
只要是继承了ViewSetMixin的类, 路由写法就变了,视图类中的方法可以随便起名,只需要在路由中映射即可.
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.IndexView.as_view({'get': 'hello'}))
]
# --- # --- # --- #
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet, GenericViewSet
# -- ReadOnlyModelViewSet: 只读,获取所有和获取单条 RetrieveModelMixin + ListModelMixin + GenericViewSet
# -- ViewSet: ViewSetMixin + APIView
# 注意,ViewSetMixin必须在前,因为需要执行的是ViewSetMixin的as_view(),而不是APIView的.
# -- GenericViewSet: ViewSetMixin + GenericAPIView
# 相比于ViewSet,不就多了可以在类里写queryset和serializer_class..
class IndexView(ViewSet):
def hello(self, request):
return Response('hello world!')
- 实验验证,get请求 http://127.0.0.1:8000/index/, 后端返回前端 "Hello World!"
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
# ★总结
两个视图基类
get post get put delete
五个视图扩展类 -- 封装了5个接口的内容,不用写内容啦
list create retrieve update destory 对应封装了 get post get put delete 的内容
九个视图子类 -- 封装了5个接口,不用写接口啦
封装/组合封装了 get post get put delete 的内容
视图集 -- 5个接口一般写两个视图类,改变下路由,只用写一个视图类啦!
需要注意的是,并没有将5个接口封装,也就是说视图类中是没有那5个接口的!!
但ViewSetMixin的存在将五个视图扩展类的list、create等方法反射成了get、post等接口!! -- 猴子补丁!!
▲ 两个视图基类
APIView -- 继承了Django的View
APIView的源码里有些类变量需要了解下
- renderer_classes: 配置的响应类
- parser_classes: 配置的解析类
- authentication_classes: 配置的认证类
- throttle_classes: 配置的频率类
- permission_classes: 配置的权限类
- versioning_class: 配置的版本类
GenericAPIView -- 继承了APIView
1> queryset -- 当前表所有数据
Books.object.all() 放在这里,压根没执行.要用时才会去拿,而且也不会全拿出来,一次20条.尽管放在这,不会影响性能!!!
2> serializer_class -- 序列化类
3> get_object() -- 获取单个
注意:路由里path转换器里的变量得命名为pk,若不想,得同步重写类变量lookup_field.
4> get_queryset() -- 获取所有数据
5> get_serializer() -- 获取序列化的类
目前了解:
filter_backends -- 过滤类
pagination_class -- 分页类
▲ 五个视图扩展类(不是视图类,它们继承的都是object类 需要和GenericAPIView一起使用)
ListModelMixin -- list方法 获取所有 -- 原来写在get里的内容
CreateModelMixin -- create方法 创建一条 -- 原来写在post中新增的代码
RetrieveModelMixin -- retrieve方法 获取一条 -- get
UpdateModelMixin -- update方法 修改一条 -- put
DestroyModelMixin -- destory方法 删除一条 -- delete
▲ 视图集 -- 仔细观察,它们命名后缀都有Set
★★★ ViewSetMixin : 继承object,重写了as_view,继承它及它的子类,路由写法就变了! .as_view({'get':'方法名'})
ViewSet : ViewSetMixin + <APIView(View)>
'GenericViewSet' : ViewSetMixin + <GenericAPIView(APIView)>
ReadOnlyModelViewSet :读相关的两个视图扩展类(ListModelMixin + RetrieveModelMixin) + 'GenericViewSet'
ModelViewSet : 五个视图扩展类 + 'GenericViewSet'
- 注意!!ViewSetMixin一定要写在最左侧!!路由写法才会变!
▲ 九个视图子类
ListAPIView = ListModelMixin + GenericAPIView + get方法,获取所有
CreateAPIView = CreateModelMixin + GenericAPIView + post方法,新增数据
RetrieveAPIView = RetrieveModelMixin + GenericAPIView + get方法,获取一条
UpdateAPIView = UpdateModelMixin + GenericAPIView + 有put和patch方法,修改数据
DestroyAPIView = DestroyModelMixin + GenericAPIView + 有delete方法,删除数据
ListCreateAPIView -- get、post
RetrieveDestroyAPIView -- get、delete
RetrieveUpdateAPIView -- get、put、patch
RetrieveUpdateDestroyAPIView -- get、put、patch、delete
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