视图
rest framework
中的两个视图基类
APIView
GenericAPIView
一、基于APIView写接口
APIView
继承自View
。
| class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="价格")
|
1. 序列化类
| from rest_framework import serializers
from app.views import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
|
2. 视图
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 | from django.shortcuts import render
# Create your views here.
from rest_framework.views import APIView
from app.models import Book
from app.serializers import BookSerializer
from rest_framework.response import Response
class BookView(APIView):
def get(self, request, pk):
book = Book.objects.filter(id=pk).first()
book_ser_obj = BookSerializer(book)
return Response(book_ser_obj.data)
def put(self, request, pk):
book = Book.objects.filter(id=pk).first()
book_ser_obj = BookSerializer(instance=book, data=request.data)
if book_ser_obj.is_valid(): # 返回True表示验证通过
book_ser_obj.save()
return Response(book_ser_obj.data)
else:
return Response({'status': 100, 'msg': '成功'})
def delete(self, request, pk):
ret = Book.objects.filter(pk=pk).delete()
return Response({'status': 100, 'msg': '成功'})
class BooksView(APIView):
def get(self, request):
books = Book.objects.all()
book_ser_obj = BookSerializer(books, many=True) # 序列化多条,如果序列化一条,不需要写
return Response(book_ser_obj.data)
def post(self, request):
book_ser_obj = BookSerializer(data=request.data)
if book_ser_obj.is_valid():
book_ser_obj.save() # 需要从写create()
return Response(book_ser_obj.data)
else:
return Response({'status': 100, 'msg': '成功'})
# 增多条数据
# if isinstance(request.data,list):
# book_ser_obj = BookSerializer(data=request.data, many=True)
# 分析源码
# 改多条数据
|
3. 路由
| from django.conf.urls import url
from django.contrib import admin
from app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^books/(?P<pk>\d)', views.BookView.as_view()),
url(r'^books/', views.BooksView.as_view()),
]
|
二、 基于GenericAPIView
写接口
看源码可知GenericAPIView
继承了APIView
:
| class GenericAPIView(views.APIView):
...
queryset = None # queryset要传queryset对象
serializer_class = None # 使用哪个序列化类来序列化数据
...
|
继承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 | from django.shortcuts import render
# Create your views here.
from rest_framework.views import APIView
from app.models import Book
from app.serializers import BookSerializer
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
class BookView(GenericAPIView):
queryset = Book.objects
serializer_class = BookSerializer
def get(self, request, pk):
# book = Book.objects.filter(id=pk).first()
book = self.get_object()
book_ser_obj = self.get_serializer(book)
return Response(book_ser_obj.data)
def put(self, request, pk):
book = self.get_object()
book_ser_obj = self.get_serializer(instance=book, data=request.data)
if book_ser_obj.is_valid(): # 返回True表示验证通过
book_ser_obj.save()
return Response(book_ser_obj.data)
else:
return Response({'status': 100, 'msg': '成功'})
def delete(self, request, pk):
ret = self.get_object().delete()
return Response({'status': 100, 'msg': '成功'})
class BooksView(GenericAPIView):
queryset = Book.objects
serializer_class = BookSerializer
def get(self, request):
# books = Book.objects.all()
books = self.get_queryset() # 看源码,这个方法就只执行的 Book.objects.all()
# book_ser_obj = BookSerializer(books, many=True) # 序列化多条,如果序列化一条,不需要写
book_ser_obj = self.get_serializer(books, many=True)
return Response(book_ser_obj.data)
def post(self, request):
# book_ser_obj = BookSerializer(data=request.data)
book_ser_obj = self.get_serializer(data=request.data)
if book_ser_obj.is_valid():
book_ser_obj.save() # 需要从写create()
return Response(book_ser_obj.data)
else:
return Response({'status': 100, 'msg': '成功'})
|
用了GenericAPIView
以后,再写其他模型的序列化,只需要修改queryset
和serializer_class
的值就可以了,两个视图中的方法(get,post,put,delete),不需要改,直接复制就可以用了。但是这种写法很冗余,可以把这五个方法封装起来,于是rest framework
就有了以下的五个视图扩展类。
三、基于GenericAPIView和5个视图扩展类写接口
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 | # views.py
from app.models import Book
from app.serializers import BookSerializer
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
RetrieveModelMixin
class BookView(GenericAPIView, RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin):
queryset = Book.objects
serializer_class = BookSerializer
def get(self, request, pk):
return self.retrieve(request, pk)
def put(self, request, pk):
return self.update(request, pk)
def delete(self, request, pk):
return self.destroy((request, pk))
class BooksView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Book.objects
serializer_class = BookSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
|
四、GenericAPIView
的9个视图子类
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 | # D:\APP\python36\Lib\site-packages\rest_framework\generics.py
# 基础的五个子类
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
...
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
...
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
...
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
...
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
...
# 组合到一起的
# get和post组合为一个
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
...
# get和delete组合为一个
class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
...
# get和put组合为一个
class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
GenericAPIView):
...
# get、put和delete组合为一个
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
...
|
使用视图子类以后的views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | # views.py
from app.models import Book
from app.serializers import BookSerializer
from rest_framework.generics import GenericAPIView, ListAPIView, CreateAPIView, UpdateAPIView, DestroyAPIView, \
RetrieveAPIView
class BookView(UpdateAPIView, DestroyAPIView, RetrieveAPIView):
queryset = Book.objects
serializer_class = BookSerializer
class BooksView(ListAPIView, CreateAPIView):
queryset = Book.objects
serializer_class = BookSerializer
|
源码阅读
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class GenericAPIView(views.APIView):
...
def get_object(self):
queryset = self.filter_queryset(self.get_queryset()) # 返回所有数据queryset对象
# Perform the lookup filtering.
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field # lookup_field = 'pk' 路由有名分组中的pk
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
# May raise a permission denied
self.check_object_permissions(self.request, obj)
return obj
|
上诉的两组接口,可以使用ModelViewSet
整合为一个。
五、ModelViewSet
1. 使用ModelViewSet
编写5个接口
| from app.models import Book
from app.serializers import BookSerializer
from rest_framework.viewsets import ModelViewSet
class BooksView(ModelViewSet): # 5个接口都有,但是路由需要特殊配置
queryset = Book.objects
serializer_class = BookSerializer
|
路由中的as_view()
需要传入参数
| from django.conf.urls import url
from django.contrib import admin
from app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^books/(?P<pk>\d)', views.BooksView.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
# 当路径匹配,又是get请求,会执行BooksView的list方法
url(r'^books/', views.BooksView.as_view(actions={'get': 'list', 'post': 'create'})),
]
|
2. ViewSetMixin
源码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class ViewSetMixin:
# 重写了as_views方法
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.action_map = actions
# 核心代码(所以路由中只要配置了对应关系,比如{'get':'list'}),当get请求来,就会执行list方法
for method, action in actions.items():
# get list
handler = getattr(self, action)
# 执行完上一句,handler就变成了list的内存地址
setattr(self, method, handler)
# 执行完上一句 对象.get=list
...
|
| # 重写了as_view
# 核心代码(所以路由中只要配置了对应关系,比如{'get':'list'}),当get请求来,就会执行list方法
for method, action in actions.items():
#method:get
# action:list
handler = getattr(self, action)
#执行完上一句,handler就变成了list的内存地址
setattr(self, method, handler)
#执行完上一句 对象.get=list
#for循环执行完毕 对象.get:对着list 对象.post:对着create
|
3 继承ViewSetMixin
的视图类
| # views.py
from rest_framework.viewsets import ViewSetMixin
class BooksView(ViewSetMixin, APIView): #一定要放在APIVIew前
def get_all_book(self,request):
print("xxxx")
book_list = Book.objects.all()
book_ser = BookSerializer(book_list, many=True)
return Response(book_ser.data)
|
路由
| # urls.py
#继承ViewSetMixin的视图类,路由可以改写成这样
url('books/', views.BooksView.as_view(actions={'get': 'get_all_book'})),
|