视图和路由

视图组件

视图继承关系

img

2 个视图基类

APIView
from rest_framework.views import APIView

属性:
	renderer_classes,parser_classes...
get方法post方法delete方法等和View的一样只是request对象变成了新的request对象
比View多了三大认证和全局异常处理
GenericAPIView
继承了APIView多了一些属性和方法
from rest_framework.generics import GenericAPIView

属性:
  queryset
  serializer_class

方法:
  get_queryset: 获取qs数据
  get_object: 获取一条数据的对象
  get_serializer: 以后使用它来实例化得到ser对象
  get_serializer_class: 获取序列化类

5 个视图扩展类

  from rest_framework.mixins
  CreateModelMixin,
  ListModelMixin,
  DestroyModelMixin,
  RetrieveModelMixin,
  UpdateModelMixin

1 查所有ListModelMixin
    列表视图扩展类提供list(request, *args, **kwargs)方法快速实现列表视图返回200状态码
     该Mixin的list方法会对数据进行过滤和分页

2 查一个RetrieveModelMixin
    创建视图扩展类提供create(request, *args, **kwargs)方法快速实现创建资源的视图成功返回201状态码
     如果序列化器对前端发送的数据验证失败返回400错误

3 增一个CreateModelMixin
    详情视图扩展类提供retrieve(request, *args, **kwargs)方法可以快速实现返回一个存在的数据对象
     如果存在返回200 否则返回404

4 改一个UpdateModelMixin
    更新视图扩展类提供update(request, *args, **kwargs)方法可以快速实现更新一个存在的数据对象
    同时也提供partial_update(request, *args, **kwargs)方法可以实现局部更新
     成功返回200序列化器校验数据失败时返回400错误

5 删一个DestroyModelMixin
    删除视图扩展类提供destroy(request, *args, **kwargs)方法可以快速实现删除一个存在的数据对象
    成功返回204不存在返回404

9 个视图子类

  from rest_framework.generics
  CreateAPIView,
  ListAPIView,
  DestroyAPIView,
  RetrieveAPIView,
  UpdateAPIView
  ListCreateAPIView,
  RetrieveUpdateAPIView,
  RetrieveUpdateDestroyAPIView,
  RetrieveDestroyAPIView

1查所有ListAPIView
提供 get 方法
继承自GenericAPIViewListModelMixin


2增一个CreateAPIView
提供 post 方法
继承自 GenericAPIViewCreateModelMixin


3查所有+增一个ListCreateAPIView
提供 get 和 post 方法
继承自 GenericAPIViewListModelMixinCreateModelMixin


4查一个RetrieveAPIView
提供 get 方法
继承自: GenericAPIViewRetrieveModelMixin


5改一个UpdateAPIView
提供 put 和 patch 方法
继承自GenericAPIViewUpdateModelMixin


6删一个DestoryAPIView
提供 delete 方法
继承自GenericAPIViewDestoryModelMixin


7查一个+改一个RetrieveUpdateAPIView
提供 getputpatch方法
继承自 GenericAPIViewRetrieveModelMixinUpdateModelMixin


8查一个+删一个RetrieveDestroyAPIView
提供 get 和 delete 方法
继承自 GenericAPIViewRetrieveModelMixinDestoryModelMixin


9 查一个+改一个+删一个RetrieveUpdateDestoryAPIView
提供 getputpatchdelete方法
继承自GenericAPIViewRetrieveModelMixinUpdateModelMixinDestoryModelMixin

视图类的使用

views
## 第一层:继承APIView写视图类

## 第二层:继承GenericAPIView写视图类
from rest_framework.generics import GenericAPIView

class PublishView(GenericAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerialzier


    def get(self, request):
        # obj = self.queryset
        obj = self.get_queryset()  # 等同于上面,更好一些
        # ser = self.serializers(instance=obj,many=True)
        # ser=self.get_serializer_class()(instance=obj,many=True) # 等同于上面
        ser = self.get_serializer(instance=obj, many=True)  # 等同于上面
        return Response(ser.data)

    def post(self, request):
        # ser = BookSerializer(data=request.data)
        ser = self.get_serializer(data=request.data)  # 等同于上面
        if ser.is_valid():
            ser.save()
            return Response({"code": 100, 'msg': '新增成功', 'data': ser.data})
        return Response({"code": 101, 'msg': '新增出错', 'err': ser.errors})


# class PublishDetailView(GenericAPIView):
#     queryset = Publish.objects.all()
#     serializer_class = PublishSerialzier
#
#     # lookup_field = 'pk'
#     def get(self, request, *args, **kwargs):
#         # book = Book.objects.all().filter(pk=pk).first()
#         obj = self.get_object()  # 等同于上面
#         # ser = BookSerializer(instance=book)
#         ser = self.get_serializer(instance=obj)  # 等同于上面
#         return Response(ser.data)
#
#     def put(self, request, *args, **kwargs):
#         # book = Book.objects.all().filter(pk=pk).first()
#         obj = self.get_object()  # 等同于上面
#         # ser = BookSerializer(instance=book, data=request.data)
#         ser = self.get_serializer(instance=obj, data=request.data)  # 等同于上面
#         if ser.is_valid():
#             ser.save()
#             return Response({"code": 100, 'msg': '修改成功', 'data': ser.data})
#         return Response({"code": 101, 'msg': '修改出错', 'err': ser.errors})
#
#     def delete(self, request, *args, **kwargs):
#         # Book.objects.filter(pk=pk).delete()
#         self.get_object().delete()
#         return Response({"code": 100, 'msg': '删除成功'})


## 第三层:GenericAPIView+5个视图扩展类
# from rest_framework.generics import GenericAPIView
# from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
#
#
#
# class PublishView(GenericAPIView,ListModelMixin,CreateModelMixin):
#     queryset = Publish.objects.all()
#     serializer_class = PublishSerialzier
#
#     # def perform_create(self,serializer):
#     #     # 判断,验证失败了,抛异常,通过了再保存
#     #     serializer.save()
#     def get(self, request):
#         return super().list(request) # create(request)ListModelMixin的方法
#
#     def post(self, request):
#         return super().create(request)  # create(request)CreateModelMixin的方法
#
#
# class PublishDetailView(GenericAPIView,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin):
#     queryset = Publish.objects.all()
#     serializer_class = PublishSerialzier
#
#     # lookup_field = 'pk'
#     def get(self, request, *args, **kwargs):
#         return super().retrieve(request, *args, **kwargs)
#
#     def put(self, request, *args, **kwargs):
#         return super().update(request, *args, **kwargs)
#
#     def delete(self, request, *args, **kwargs):
#         return super().destroy(request, *args, **kwargs)


# 第四层:通过9个视图子类,编写视图函数
# from rest_framework.generics import CreateAPIView, ListAPIView, DestroyAPIView, RetrieveAPIView, UpdateAPIView
# from rest_framework.generics import ListCreateAPIView, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView, \
#     RetrieveDestroyAPIView
#
#
# # class PublishView(ListCreateAPIView):  # 查询所有和新增接口就有了
# # class PublishView(CreateAPIView):  # 新增接口就有了
# class PublishView(ListAPIView):  # 查询所有接口就有了
#     queryset = Publish.objects.all()
#     serializer_class = PublishSerialzier
#
#     # 有可能要重写--》get_queryset--》get_serializer_class--》perform_create--》get,post方法
#
#
# class PublishDetailView(RetrieveUpdateDestroyAPIView): # 查询单条,删除,修改
# # class PublishDetailView(RetrieveAPIView): # 查询单条
# # class PublishDetailView(DestroyAPIView): # 删除
# # class PublishDetailView(UpdateAPIView): # 修改
# # class PublishDetailView(RetrieveDestroyAPIView): # 查询单条和删除
# # class PublishDetailView(RetrieveUpdateAPIView): # 查询单条和更新
# # class PublishDetailView(UpdateAPIView,DestroyAPIView): # 更新和删除
#     queryset = Publish.objects.all()
#     serializer_class = PublishSerialzier



# 第五层:通过ViewSet写视图类

# 5个接口,都用一个视图类----》路由 有两个get
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
# ViewSet=APIView+ViewSetMixin
# GenericViewSet=GenericAPIView+ViewSetMixin
# 以后只要想自动生成路由,必须继承ViewSetMixin及其子类
# 之前的写法可以沿用,只是如果要自动生成路由可以选择继承ViewSet,GenericViewSet
from rest_framework.viewsets import ViewSet,GenericViewSet
from rest_framework.viewsets import ViewSetMixin

# 继承了5个视图扩展类+GenericViewSet(ViewSetMixin, generics.GenericAPIView)
# ViewSetMixin-->控制了路由写法变了
# class PublishView(ModelViewSet):  # 修改路由,5个接口
# class PublishView(ReadOnlyModelViewSet):  # 修改路由,只读,查所有,查单个
#     queryset = Publish.objects.all()
#     serializer_class = PublishSerialzier
models
class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()
serializer
class PublishSerialzier(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = '__all__'
urls
path('publishs/', views.PublishView.as_view()),
path('publishs/<int:pk>', views.PublishDetailView.as_view()),

视图集

  from rest_framework.viewsets
  # 两个视图类
  ModelViewSet,ReadOnlyModelViewSet
  # 视图类
  ViewSet,GenericViewSet,
  # 魔法类
  ViewSetMixin

1 ViewSet
继承自APIView与ViewSetMixin作用也与APIView基本类似提供了身份认证权限校验流量管理等

ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典如{get:list}的映射处理工作

在ViewSet中没有提供任何动作action方法需要我们自己实现action方法

2GenericViewSet
使用ViewSet通常并不方便因为listretrievecreateupdatedestory等方法都需要自己编写而这些方法与前面讲过的Mixin扩展类提供的方法同名所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写但是Mixin扩展类依赖与GenericAPIView所以还需要继承GenericAPIView

GenericViewSet就帮助我们完成了这样的继承工作继承自GenericAPIView与ViewSetMixin在实现了调用as_view()时传入字典如{'get':'list'}的映射处理工作的同时还提供了GenericAPIView提供的基础方法可以直接搭配Mixin扩展类使用

3ModelViewSet
继承自GenericViewSet同时包括了ListModelMixinRetrieveModelMixinCreateModelMixinUpdateModelMixinDestoryModelMixin

4ReadOnlyModelViewSet
继承自GenericViewSet同时包括了ListModelMixinRetrieveModelMixin

视图总结

# 两个基类
APIView
GenericAPIView有关数据库操作queryset 和serializer_class


# 5个视图扩展类(rest_framework.mixins)
CreateModelMixincreate方法创建一条
DestroyModelMixindestory方法删除一条
ListModelMixinlist方法获取所有
RetrieveModelMixinretrieve获取一条
UpdateModelMixinupdate修改一条

# 9个子类视图(rest_framework.generics)
CreateAPIView:继承CreateModelMixin,GenericAPIView有post方法新增数据
DestroyAPIView继承DestroyModelMixin,GenericAPIView有delete方法删除数据
ListAPIView继承ListModelMixin,GenericAPIView,有get方法获取所有
UpdateAPIView继承UpdateModelMixin,GenericAPIView有put和patch方法修改数据
RetrieveAPIView继承RetrieveModelMixin,GenericAPIView有get方法获取一条


ListCreateAPIView继承ListModelMixin,CreateModelMixin,GenericAPIView有get获取所有post方法新增
RetrieveDestroyAPIView继承RetrieveModelMixin,DestroyModelMixin,GenericAPIView有get方法获取一条delete方法删除
RetrieveUpdateAPIView继承RetrieveModelMixin,UpdateModelMixin,GenericAPIView有get获取一条putpatch修改
RetrieveUpdateDestroyAPIView继承RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView有get获取一条putpatch修改delete删除

# 视图集
ViewSetMixin重写了as_view
ViewSet     继承ViewSetMixin和APIView

GenericViewSet继承ViewSetMixin, generics.GenericAPIView
ModelViewSet继承mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,GenericViewSet
ReadOnlyModelViewSet继承mixins.RetrieveModelMixin,mixins.ListModelMixin,GenericViewSet
1 序列化器源码
    -many参数控制在__new__中控制了对象的生成
    -局部和全局钩子源码is_valid--找self.方法一定要从根上找
    -source参数是如何执行的:‘publish.name,'方法'

2 视图
    -2个视图基类
    -5个视图扩展类
    -9个视图子类
    -视图集
        -ViewSetMixin
        -ViewSet, GenericViewSet
        -ModelViewSet, ReadOnlyModelViewSet

3 视图类的继承原则
    -如果不涉及数据库操作继承APIView
    -如果想让路由可以映射继承ViewSetMixin
    -如果不涉及数据库操作又要让路由可以映射继承ViewSet
    -比如发邮件接口发短信接口

    -如果涉及到数据库操作继承GenericAPIView
    -如果想让路由可以映射继承ViewSetMixin
    -如果涉及数据库操作又要让路由可以映射继承GenericViewSet
    -如果涉及数据库操作又要让路由可以映射还要能新增继承GenericViewSet+CreateModelMixin或者继承ViewSetMixin+CreateAPIView

    -如果只涉及到数据库操作和新增继承CreateAPIView

    -路由有映射数据库操作3个接口查一个删一个改一个

4 ViewSetMixin路由的写法就特殊了


5 类实例化先执行了元类的__call__:调用了这个类的__new__,生成一个空对象调用了类的__init__,完成了对象的初始化
6 对象()---->会触发类的 __call__
7 类()----->会触发类的类元类的__call__

image-20220401204213872

路由组件

三种路由写法

# 以后路由写法有三种
	path('books/<int:pk>', views.BookDetailView.as_view()),
  # 继承了ViewSetMixin
  path('publish/', views.PublishView.as_view({'get':'list','post':'create'}))
  # 自动生成路由
  from rest_framework.routers import SimpleRouter,DefaultRouter
  router = SimpleRouter()
  router.register('publish', views.PublishView, 'publish')
	# 路由注册,两种方式
  path('api/v1/', include(router.urls)),
  urlpatterns+=router.urls
  # 路由类,有连个SimpleRouter,DefaultRouter,DefaultRouter自动生成的路由更多

自动生成路由

# 只要继承ViewSetMixin 及其子类,路由写法就变了
# 视图类:继承ViewSetMixin,路由写法变了--->而且视图类中的方法不一定写成get,post..,可以随意命名,只不过定义路由时写法变成了path('test/', views.TestView.as_view({'get': 'login'})),get请求执行login方法

# 到底如何执行的
  # ViewSetMixin必须写在前面
  ViewSetMixin ---> as_view--->类的查找顺序 ---> actions就是传入的字典 ---> view闭包函数
  def view(request, *args, **kwargs):
     #    get      login
    	for method, action in actions.items():
        # 把login方法的内存地址给了handler
        handler = getattr(self, action)
        # 通过反射,设置给get ---> 对应login ---> get请求执行get方法,现在get方法变成了login方法
        setattr(self, method, handler)
      return self.dispatch(request, *args, **kwargs)# 跟之前一样了


# 视图类,继承ModelViewSet,路由写法
    path('publish/', views.PublishView.as_view({'get':'list','post':'create'})),
    path('publish/<int:pk>', views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'}))

    # 到现在,咱们有了一种方式,可以在一个视图类中写很多方法,通过路由的配置,实现一个视图函数对应很多路由

    # 上面的两个路由,可以自动生成--->生成上面那两条地址
    	第一步导入drf提供的路由类
      from rest_framework.routers import SimpleRouter
      第二步实例化得到对象
      router = SimpleRouter()
      第三步注册路由
      router.register('地址', 视图类, '别名')
      router.register('publish', views.PublishView, 'publish')
      第四步在urlpatterns加入两种方式
      	path('/api/v1', include(router.urls))
        urlpatterns+=router.urls


   # SimpleRouter和DefaultRouter 用法完全一样,只是生成的路由不一样DefaultRouter比SimpleRouter多一条根地址,一般咱么就用SimpleRouter就可以


  # 如果使用自动生成路由,必须继承谁及其子类?
    GenericViewSet+5个视图扩展类至少之一---才能自动生成
    ModelViewSet
    ReadOnlyModelViewSet

action装饰器

 from rest_framework.decorators import action
  # action装饰器---》只要继承ViewSetMixin都能自动生成路由
    # methods=请求方法,列表,
    # detail=是否带id,
    # url_path=地址如果不写,默认已方法名为地址,
    # url_name=别名
    # 写成如下,自动生成路由会生成一条路径:127.0.0.1:8000/test/login -->get,post都会触发
    @action(methods=['GET','POST'],detail=True)
    def login(self,request):

   # 如果写法如下,生成的路径是127.0.0.1:8000/test/数字/login
    @action(methods=['GET','POST'],detail=True)
    def login(self,request):

 # 通过action,怎么通过action判断,重写get_queryset,get_serializer_class

简单案例

# 视图及自动生成路由
# views.py
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
    queryset = models.Books.objects.all()
    serializer_class = serializer.BookSerializer

# models.py
from django.db import models
class Books(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE)
    authors = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.name

    @property
    def publish_list(self):
        return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}

    @property
    def authors_list(self):
        return [{'name': author.name, 'age': author.age, 'phone': author.author_detail.tel,
                 'address': author.author_detail.addr} for author in self.authors.all()]

# serializer.py
from rest_framework import serializers
from app01 import models
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Books
        fields = "__all__"
        extra_kwargs = {
            "publish": {'write_only': True},
            "authors": {'write_only': True},
        }

    publish_list = serializers.DictField(read_only=True)
    authors_list = serializers.ListField(read_only=True)

# urls.py
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('books', views.BookView)

urlpatterns = [
    path('api/v1/', include(router.urls)),
]
## action使用
# views.py
from rest_framework.viewsets import GenericViewSet
class TestView(GenericViewSet):
    @action(methods=['GET'], detail=False)
    def test(self, request):
        return Response("get-test")

# urls.py
    path('test/', views.TestView.as_view({'get': 'test'})),