跳转至

视图

rest framework中的两个视图基类

  1. APIView
  2. GenericAPIView

一、基于APIView写接口

APIView继承自View

1
2
3
class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="价格")

1. 序列化类

1
2
3
4
5
6
7
8
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. 路由

1
2
3
4
5
6
7
8
9
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

1
2
3
4
5
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以后,再写其他模型的序列化,只需要修改querysetserializer_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个接口

1
2
3
4
5
6
7
8
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()需要传入参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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
            ...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 重写了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的视图类

1
2
3
4
5
6
7
8
# 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)

路由

1
2
3
# urls.py
    #继承ViewSetMixin的视图类,路由可以改写成这样
    url('books/', views.BooksView.as_view(actions={'get': 'get_all_book'})),