Django-Rest-Framework的视图和路由

Django-Rest-Framework的视图

APIView

django中写CBV的时候继承的是View,rest_framework继承的是APIView,那么他们两个有什么不同呢

urlpatterns = [
    url(r'^book$', BookView.as_view()),
    url(r'^book/(?P<id>\d+)$', BookEditView.as_view()),
]

可以看到,不管是View还是APIView最开始调用的都是as_view()方法,那走进源码看看

我们能看到,APIView继承了View, 并且执行了View中的as_view()方法,最后把view返回了,用csrf_exempt()方法包裹后去掉了csrf的认证。

那我们看看View中的as_view()方法做了什么
我们看到了~在View中的as_view方法返回了view函数,而view函数执行了self.dispatch()方法~~但是这里的dispatch方法应该是我们APIView中的
我们去initialize_request中看下把什么赋值给了request,并且赋值给了self.request, 也就是我们在视图中用的request.xxx到底是什么
我们看到,这个方法返回的是Request这个类的实例对象
我们注意我们看下这个Request类中的第一个参数request,是我们走我们django的时候的原来的request~
我们看到了,这个Request类把原来的request赋值给了self._request, 也就是说以后_request是我们老的request,新的request是我们这个Request类

那我们继承APIView之后请求来的数据都在哪呢~~
我们用了rest_framework框架以后,我们的request是重新封装的Request类~

request.query_params 存放的是我们get请求的参数

request.data 存放的是我们所有的数据,包括post请求的以及put,patch请求

相比原来的django的request,我们现在的request更加精简,清晰了~~~

现在我们知道了APIView和View的一些区别~~当然还有~~后面还会说~~

我们写的视图可能对多个表进行增删改查,就导致我们的视图特别多重复的代码~~

那么我们尝试着来进行封装一下~~

第一次封装

APIView视图

class BookView(APIView):

    def get(self, request):
        query_set = Book.objects.all()
        book_ser = BookSerializer(query_set, many=True)
        return Response(book_ser.data)

    def post(self, request):
        query_set = request.data
        book_ser = BookSerializer(data=query_set)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)


class BookEditView(APIView):

    def get(self, request, id):
        query_set = Book.objects.filter(id=id).first()
        book_ser = BookSerializer(query_set)
        return Response(book_ser.data)

    def patch(self, request, id):
        query_set = Book.objects.filter(id=id).first()
        book_ser = BookSerializer(query_set, data=request.data, partial=True)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)

    def delete(self, request, id):
        query_set = Book.objects.filter(id=id).first()
        if query_set:
            query_set.delete()
            return Response("")
        else:
            return Response("删除的书籍不存在")

第一次封装

class GenericAPIView(APIView):
    queryset = None
    serializer_class = None

    def get_queryset(self):
        return self.queryset.all()

    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)


class ListModelMixin(object):
    def list(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)


class CreateModelMixin(object):
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.validated_data)
        else:
            return Response(serializer.errors)


class RetrieveModelMixin(object):
    def retrieve(self, request, id, *args, **kwargs):
        book_obj = self.get_queryset().filter(pk=id).first()
        book_ser = self.get_serializer(book_obj)
        return Response(book_ser.data)


class UpdateModelMixin(object):
    def update(self, request, id, *args, **kwargs):
        book_obj = self.get_queryset().filter(pk=id).first()
        book_ser = self.get_serializer(book_obj, data=request.data, partial=True)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)


class DestroyModelMixin(object):
    def destroy(self, request, id, *args, **kwargs):
        queryset = self.get_queryset()
        try:
            queryset.get(pk=id).delete()
            return Response("")
        except Exception as e:
            return Response("信息有误")
# 我们把公共的部分抽出来 这样不管写多少表的增删改查都变的很简单
# 这样封装后我们的视图会变成这样

class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, id, *args, **kwargs):
        return self.retrieve(request, id, *args, **kwargs)

    def patch(self, request, id, *args, **kwargs):
        return self.update(request, id, *args, **kwargs)

    def destroy(self, request, id, *args, **kwargs):
        return self.delete(request, id, *args, **kwargs)

我们封装的GenericAPIView,包括封装每个方法的类,其实框架都帮我们封装好了~~

我们可以直接继承这些类来实现上面的视图可是还有没有更简单的方法呢~我们再次封装一下~~

第二次封装

# 上面我们写的继承类太长了~~我们再改改

class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
    pass


class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    pass


class BookView(ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookEditView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, id, *args, **kwargs):
        return self.retrieve(request, id, *args, **kwargs)

    def patch(self, request, id, *args, **kwargs):
        return self.update(request, id, *args, **kwargs)

    def delete(self, request, id, *args, **kwargs):
        return self.delete(request, id, *args, **kwargs)

这次我们只是让继承变的简单了一点而已,好像并没有什么大的进步
我们可不可以把这两个视图合并成一个视图呢~~~框架给我们提供了一个路由传参的方法

我们看下ViewSetMixin

action这个默认参数其实就是我们路由可以进行传参了

下面这个循环~可以看出~我们要传的参数是一个字段~key应该是我们的请求方式,value应该对应我们处理的方法~

这样我们每个视图就不用在写函数了~因为已经和内部实现的函数相对应了~

第三次封装

路由 urls.py

urlpatterns = [
    # url(r'^book$', BookView.as_view()),
    # url(r'^book/(?P<id>\d+)$', BookEditView.as_view()),
    url(r'^book$', BookView.as_view({"get": "list", "post": "create"})),
    url(r'^book/(?P<pk>\d+)$', BookView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})),
]

第三次封装

from rest_framework.viewsets import ViewSetMixin

# class BookView(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
#     queryset = Book.objects.all()
#     serializer_class = BookSerializer


# 如果我们再定义一个类
class ModelViewSet(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
    pass


class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

我们现在的视图就只要写两行就可以了

其实我们写的所有的视图~框架都帮我们封装好了

注意一点~用框架封装的视图~我们url上的那个关键字参数要用pk~系统默认的

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

Django-Rest-Framework的路由

我们上面的路由传参写的特别多~~框架也帮我们封装好了~

from .views import BookView
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"book", BookView)

urlpatterns = [
    # url(r'^book$', BookView.as_view()),
    # url(r'^book/(?P<id>\d+)$', BookEditView.as_view()),
    # url(r'^book$', BookView.as_view({"get": "list", "post": "create"})),
    # url(r'^book/(?P<pk>\d+)$', BookView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})),

]
urlpatterns += router.urls

我们可以看到通过框架我们可以把路由视图都变的非常简单~~

但是需要自定制的时候还是需要我们自己用APIView写,当不需要那么多路由的时候,也不要用这种路由注册~~

总之一切按照业务需要去用~~~

总结

urls.py

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
from serdemo import views
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'book', views.BookModelView)

urlpatterns = [
# 第一次
# url(r'^book/$', views.BookView.as_view()),
# url(r'^book/(?P<id>\d+)', views.BookEditView.as_view()),

# 第二次
# url(r'^book/$', views.BookView.as_view()),
# url(r'^book/(?P<id>\d+)', views.BookEditView.as_view()),

# 第三次
# url(r'^book/$', views.BookModelView.as_view({"get": "list", "post": "create"})),
# url(r'^book/(?P<id>\d+)', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),


# url(r'book_page$', views.PageBookView.as_view())
]

# 第三次
urlpatterns += router.urls

views.py

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
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
147
148
149
from demo import models
from rest_framework.views import APIView
from rest_framework.response import Response
from serdemo import serializers
from rest_framework.viewsets import ViewSetMixin

from rest_framework import views # APIView
from rest_framework import viewsets
from rest_framework import generics
from rest_framework import mixins
from .pagination import MyPaginator, MyCursorPagination, MyLimitOffset


# queryset不同 序列化器不同
# def get:pass
# def post:pass


class GenericAPIView(APIView):
queryset = None
serializer_class = None

def get_queryset(self):
return self.queryset.all()

def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)


class ListModelMixin(object):
def list(self, request):
queryset = self.get_queryset()
ser_obj = self.get_serializer(queryset, many=True)
return Response(ser_obj.data)


class CreateModelMixin(object):
def create(self, request):
ser_obj = self.get_serializer(data=request.data)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data)
return Response(ser_obj.errors)


class RetrieveModelMixin(object):
def retrieve(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
ser_obj = serializers.BookSerializer(book_obj)
return Response(ser_obj.data)


class UpdateModelMixin(object):
def update(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
ser_obj = self.get_serializer(instance=book_obj, data=request.data, partial=True)
if ser_obj.is_valid():
ser_obj.save()
return Response(ser_obj.data)
return Response(ser_obj.errors)


class DestroyModelMixin(object):
def destroy(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
if not book_obj:
return Response("删除的对象不存在")
book_obj.delete()
return Response("")


class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
pass


class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass


class ModelViewSet(ViewSetMixin, ListModelMixin, RetrieveUpdateDestroyAPIView):
pass


# get post
# 封装一
# class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
# queryset = models.Book.objects.all()
# serializer_class = serializers.BookSerializer
#
# def get(self, request):
# return self.list(request)
#
# def post(self, request):
# return self.create(request)


# get put delete
# 封装一
# class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
# queryset = models.Book.objects.all()
# serializer_class = serializers.BookSerializer
#
# def get(self, request, id):
# return self.retrieve(request, id)
#
# def put(self, request, id):
# return self.update(request, id)
#
# def delete(self, request, id):
# return self.destroy(request, id)


# # 分装二
# class BookView(ListCreateAPIView):
# queryset = models.Book.objects.all()
# serializer_class = serializers.BookSerializer
#
# def get(self, request):
# return self.list(request)
#
# def post(self, request):
# return self.create(request)
#
#
# # 封装二
# class BookEditView(RetrieveUpdateDestroyAPIView):
# queryset = models.Book.objects.all()
# serializer_class = serializers.BookSerializer
#
# def get(self, request, id):
# return self.retrieve(request, id)
#
# def put(self, request, id):
# return self.update(request, id)
#
# def delete(self, request, id):
# return self.destroy(request, id)


# 分装三 BookModelView
class BookModelView(viewsets.ModelViewSet): # 只需要在路由中指定字典就可以
queryset = models.Book.objects.all()
serializer_class = serializers.BookSerializer
# 分页一
pagination_class = MyPaginator
# 分页二
# pagination_class = MyLimitOffset
# 分页三
# pagination_class = MyCursorPagination

serializers.py

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
class BookSerializer(serializers.ModelSerializer):
# 重写正序
category_info = serializers.SerializerMethodField(read_only=True)
publisher_info = serializers.SerializerMethodField(read_only=True)
authors_info = serializers.SerializerMethodField(read_only=True)

def get_category_info(self, obj):
# obj 就是序列化的每一个Book对象
return obj.get_category_display()

def get_publisher_info(self, obj):
# obj 就是序列化的每一个Book对象
publisher_obj = obj.publisher
return {"id": publisher_obj.pk, "title": publisher_obj.title}

def get_authors_info(self, obj):
# obj 就是序列化的每一个Book对象
author_qureryset = obj.authors.all()
return [{"id": author_obj.pk, "name": author_obj.name} for author_obj in author_qureryset]

class Meta:
model = models.Book
fields = "__all__"
# exclude=["id"]
# 会让所有的外键关系变成只读read_only=True
# depth = 1   # 向下找几层
# 反序列化的时候不用自己定义的,而是还是用原来的字段
extra_kwargs = {"title": {"validators": [my_validate]}, "publisher": {"write_only": True}, "authors": {"write_only": True},
"category": {"write_only": True}}

# 验证
# 局部钩子校验 单个字段
def validate_title(self, value):
# value 就是title 的值 对value处理
if "python" not in value.lower():
raise serializers.ValidationError('标题必须包含python')
return value

# 全局钩子校验 全部字段
def validate(self, attrs):
# attr 字典有你传过来的所有的字段
if "python1" in attrs["title"].lower():
return attrs
else:
raise serializers.ValidationError("分类或标题不合符要求")
感谢您的支持!学如逆水行舟,不进则退!