64、5个视图扩展类、9个视图之类、视图集、归纳、drf之路由

206 阅读9分钟

5个视图扩展类

自己写的

class ListModelMixin:
    def list(self, request,*args,**kwargs):
        qs = self.get_queryset()
        ser = self.get_serializer(qs, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

class CreateModelMixin:
    def create(self, request,*args,**kwargs):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data)
            ser.save()
            return Response({'code': 100, 'msg': '成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

class RetrieveModelMixin:
    def retrieve(self,request,*args,**kwargs):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

class UpdateModelMixin:
    def update(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({'code': 100, 'msg': '更新成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class DestroyModelMixin:
    def destroy(self,request,*args,**kwargs):
        self.get_object().delete()
        return Response({'code': 100, 'msg': '删除成功'})


from .serializer import BookSerialzier
from rest_framework.response import Response
from .models import Book
from rest_framework.generics import GenericAPIView
class BookView(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier
    
    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 = BookSerialzier
    
    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)

rest_framework.mixins中的


from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin
from .serializer import BookSerialzier
from rest_framework.response import Response
from .models import Book
from rest_framework.generics import GenericAPIView

class BookView(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier
    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 = BookSerialzier
    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)

总结

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

1. 5个视图扩展类,不是视图类,必须配合GenericAPIView及其子类使用,不能配合APIView使用
2. 5个视图扩展类,每一个类中只有一个方法,完成5个接口中的其中一个,想写多个接口,就要继承多个

3.5个视图扩展类
	1.ListModelMixin:列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。
		该Mixin的list方法会对数据进行过滤和分页。
    
 	2.CreateModelMixin:创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。
		如果序列化器对前端发送的数据验证失败,返回400错误。
    
  3.RetrieveModelMixin:详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。
		如果存在,返回200, 否则返回4044.UpdateModelMixin:更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。
		同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。
		成功返回200,序列化器校验数据失败时,返回400错误。
    
  5.DestroyModelMixin:删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。
		成功返回204,不存在返回404

9个视图子类

自己写的

class ListModelMixin:
    def list(self, request,*args,**kwargs):
        qs = self.get_queryset()
        ser = self.get_serializer(qs, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

class CreateModelMixin:
    def create(self, request,*args,**kwargs):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data)
            ser.save()
            return Response({'code': 100, 'msg': '成功'})
        else:

            return Response({'code': 100, 'msg': ser.errors})

class RetrieveModelMixin:
    def retrieve(self,request,*args,**kwargs):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

class UpdateModelMixin:

    def update(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({'code': 100, 'msg': '更新成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class DestroyModelMixin:
    def destroy(self,request,*args,**kwargs):
        self.get_object().delete()
        return Response({'code': 100, 'msg': '删除成功'})

      
from rest_framework.generics import GenericAPIView
class ListAPIView(ListModelMixin,GenericAPIView):
    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)

class CreateAPIView(CreateModelMixin,GenericAPIView):
    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)

class RetrieveAPIView(RetrieveModelMixin,GenericAPIView):
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

class UpdateAPIView(UpdateModelMixin,GenericAPIView):
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

class DestroyAPIView(DestroyModelMixin,GenericAPIView):
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


class ListCreateAPIView(ListModelMixin,CreateModelMixin,GenericAPIView):
    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 RetrieveDestroyAPIView(RetrieveModelMixin,DestroyModelMixin,GenericAPIView):
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

class RetrieveUpdateAPIView(RetrieveModelMixin,UpdateModelMixin,GenericAPIView):
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

class RetrieveUpdateDestroyAPIView(RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView):
    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)


from .serializer import BookSerialzier
from rest_framework.response import Response
from .models import Book

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

class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier
    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)

rest_framework.generics中的


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

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

class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier
    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)

总结

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

1.CreateAPIView:提供 post 方法
	继承自: GenericAPIView、CreateModelMixin

2.ListAPIView:提供 get 方法
	继承自:GenericAPIView、ListModelMixin

3.RetrieveAPIView:提供 get 方法
	继承自: GenericAPIView、RetrieveModelMixin

4.DestoryAPIView:提供 delete 方法
	继承自:GenericAPIView、DestoryModelMixin

5.UpdateAPIView:提供 put 和 patch 方法
	继承自:GenericAPIView、UpdateModelMixin

6.RetrieveUpdateAPIView:提供 get、put、patch方法
	继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

7.RetrieveUpdateDestoryAPIView:提供 get、put、patch、delete方法
	继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
  
8.ListCreateAPIView:提供get、create方法
	继承自:GenericAPIView、ListModelMixin,CreateModelMixin
  
9.RetrieveDestroyAPIView:	提供get、delete方法
	继承自:GenericAPIView、RetrieveModelMixin,DestroyModelMixin
  

视图集

案例

views.py

from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier
		# 扩展性强
    def list(self, request, *args, **kwargs):
        res = super().list(request, *args, **kwargs)
        return Response({'code': 100, 'msg': '成功', 'data': res.data})

​ urls.py

from django.contrib import admin
from django.urls import path
from app01.views import BookView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', BookView.as_view({'get':'list','post':'create'})),
    path('books/<int:pk>/', BookView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
]

用法

ModelViewSet:from rest_framework.viewsets import ModelViewSet
1.继承ModelViewSet后,只需要在视图类中写两行
    	queryset = Book.objects.all()
    	serializer_class = BookSerialzier
2.配置路由,5个接口都有了
    	path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),
    	path('books/<int:pk>/', BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

ModelViewSet 源码分析

1.ModelViewSet继承了:mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,GenericViewSet
	ps:GenericViewSet	继承了:ViewSetMixin(重写了as_view),GenericAPIView

    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        pass
      
		class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        pass
    
2.入口:path('books/', BookView.as_view({'get': 'list', 'post': 'create'}))

3.只要继承了ModelViewSet,路由写法变了,谁控制它变的:ViewSetMixin

4.ViewSetMixin如何控制路由写法变了?
		BookView.as_viewde的执行,其实是ViewSetMixin的as_view

1.鼠标点进:BookView.as_view({'get': 'list', 'post': 'create'})

# 进入到了ViewSetMixin的as_view()方法

class ViewSetMixin:
    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")
            
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)  # 是BookView的对象
            for method, action in actions.items():  # actions = {'get': 'list', 'post': 'create'}-->method='get'  action='list'
                handler = getattr(self, action)  # 	反射,取BookView的对象中反射list
                setattr(self, method, handler)  # setattr(self, 'get', list方法)  
            return self.dispatch(request, *args, **kwargs)  # APIview的dispatch
        return csrf_exempt(view)   # 避免了csrf认证

总结

1.只要继承了ViewSetMixin及其子类,路由写法就变了,必须传actions参数
2.变成映射关系了:
	path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),
3.以后,只要是books路由匹配成功,如果是get请求,就会执行视图类BookView的list方法

4.以后视图类中的方法名,可以随意命名
	eg:
    class SmsView(ViewSet):
    def ln(self,request):
        return Response('hello')
      
     from app01.views import BookView,SmsView

    urlpatterns = [
        path('ln/', SmsView.as_view({'get': 'ln'})),
    ]

5 ViewSetMixin,必须配合视图类使用(APIView,GenericAPIView,9个视图子类),必须放在视图类之前
7.ViewSetMixin+APIView=ViewSet:之前继承APIView的,但是现在想路由写法变了(自动生成路由)
8.GenericAPIView+ViewSetMixin=GenericViewSet 之前继承GenericAPIView的,但是现在想路由写法变了(自动生成路由)
9.ReadOnlyModelViewSet:获取所有和获取单条
10.ModelViewSet:5个接口,加路由变了

viewset中的类

from  rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,GenericViewSet,ViewSet,ViewSetMixin

class ViewSet(ViewSetMixin, views.APIView):
    pass

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    pass

class ReadOnlyModelViewSet(mixins.RetrieveModelMixin, 
                           mixins.ListModelMixin,
                           GenericViewSet):
     #只读视图类---> 只有两个,查询所有和查询单条                    
    pass

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    pass
  
"""
ViewSet --->ViewSetMixin + views.APIView
GenericViewSet --->ViewSetMixin + generics.GenericAPIView
ReadOnlyModelViewSet ---> mixins.RetrieveModelMixin+mixins.ListModelMixin+GenericViewSet
ModelViewSet --->mixins.CreateModelMixin+mixins.RetrieveModelMixin+mixins.UpdateModelMixin+mixins.DestroyModelMixin+mixins.ListModelMixin+GenericViewSet
"""

归纳

1. 两个视图基类
	1.APIView
  2.GenericAPIView
  
2. 5个视图扩展类(不是视图类,需要配合GenericAPIView及其子类使用)
	from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin
  
3. 9个视图子类
	from rest_framework.generics import ListAPIView,CreateAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView
  from rest_framework.generics import ListCreateAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView

4.视图集:
	  1.ModelViewSet:5个接口+GenericViewSet
    2.ReadOnlyModelViewSet:两个接口(list+retrieve)+GenericViewSet l
    3.ViewSetMixin:魔法,不能单独使用,必须配合视图类用,路由写法变了
    4.ViewSet:ViewSetMixin+APIView,以后想继承APIView,但是想路由写法变化,视图类中方法可以任意命名
    5.GenericViewSet:ViewSetMixin+GenericAPIView,以后想继承GenericAPIView,但是想路由写法变化,视图类中方法可以任意命名

drf之路由

1.路由写法
	1.原始写法
  2.映射的写法:
  	path('books/', BookView.as_view({'get': 'list', 'post': 'create'}))
  3.自动生成路由
  

自动生成路由

方法

方式一

from django.contrib import admin
from django.urls import path
from app01.views import BookView

1.第一步:导入一个路由类
from rest_framework.routers import SimpleRouter

2.第二步:实力化得到对象
router = SimpleRouter()

3.第三步:注册路由
# register(self, prefix, viewset, basename=None)
router.register('books',BookView,'books')

4.第四步:加入到路由中
  urlpatterns = [
      path('admin/', admin.site.urls),
  ]

urlpatterns += router.urls

方式二:利用include


from django.contrib import admin
from django.urls import path,include
from app01.views import BookView,SmsView

1.第一步:导入一个路由类
from rest_framework.routers import SimpleRouter

2.第二步:实力化得到对象
router = SimpleRouter()

3.第三步:注册路由
	# register(self, prefix, viewset, basename=None)
	router.register('books',BookView,'books')

4.第四步:加入到路由中
urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/',include(router.urls))
]

api/v1/ ^books/$ [name='books-list']
api/v1/ ^books/(?P<pk>[^/.]+)/$ [name='books-detail']

自动生成路由

1.继承了 5个视图扩展类+ViewSetMixin的视图类,能自动生成路由(get:list,get:retrieve..)
2.我们自己命名的:方法名:login  send_sms,需要使用装饰器来做
		eg:
    # views.py
    from rest_framework.decorators import action
    class SmsView(ViewSet):
        @action(methods=["GET"],detail=False)
        def ln(self,request):
            return Response('hello')

		# urls.py
    from django.contrib import admin
    from django.urls import path,include
    from app01.views import SmsView

    from rest_framework.routers import SimpleRouter
    router = SimpleRouter()
    router.register('ln',SmsView,'ln')
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/v1/',include(router.urls))
    ]

    """
    admin/
    api/v1/ ^ln/ln/$ [name='ln-ln']
    """
    
    
    eg:
    # views.py
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.decorators import action
    class BookView(ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookSerialzier
        @action(methods=['GET'],detail=True)
        def login(self,request):
            return Response('登陆成功')
    
    # urls.py
    from django.contrib import admin
    from django.urls import path,include
    from app01.views import BookView,SmsView

    from rest_framework.routers import SimpleRouter

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



  urlpatterns = [
      path('admin/', admin.site.urls),
      path('api/v1/',include(router.urls))
  ]
    """
    api/v1/ ^books/$ [name='books-list']
    api/v1/ ^books/(?P<pk>[^/.]+)/$ [name='books-detail']
    api/v1/ ^books/(?P<pk>[^/.]+)/login/$ [name='books-login']
    """
        
 3 action装饰器的参数
   action(methods=None, detail=None, url_path=None, url_name=None, **kwargs)
	1.methods:请求方式
  2.detail:一个True,一个False,用True,表示生成详情的路径 <int:pk>
    		True,books/1/方法名/
        False,books/方法名/
  3.url_path:路径名字,需要加上前面的路径一起,如果不加,默认以函数名作为路径名
  4.url_name:反向解析使用的名字(用的不多)
  
4.路由类,有两个,用法完全一致,区别是DefaultRouter生成的路径多
		from rest_framework.routers import SimpleRouter,DefaultRouter
	  1.SimpleRouter :用的最多
    2.DefaultRouter
    ps:DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。
    """
    {"books":"http://127.0.0.1:8081/api/v1/books/"}
    """

补充

1.序列化类
	1.之前咱么用的
		if serializer.is_valid():
        	校验通过去保存
 	2.源码中这样用(这个如果是False,直接抛异常)
    	serializer.is_valid(raise_exception=True)
    
  ps:源码:* 表示只是占位,不接受参数,表示从它往后的参数,都必须用关键字传参
     is_valid(self, *, raise_exception=False)

作业

写个login接口,自动生成路由,post请求,携带数据,能够实现登录

# views.py
from rest_framework.decorators import action
from .serializer import UserSerialzer
from .models import User
class UserView(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerialzer
    @action(methods=['POST'],detail=False)
    def login(self,request):
        print(request.data)  # {'username': 'xiao', 'password': '123'}
        res = self.get_queryset().filter(username = request.data.get('username'),password = request.data.get('password')).first()
        if not res:
            return Response({'code':101,'msg':'用户名或密码错误'})
        return Response({'code':100,'msg':'登陆成功'})


# serializer.py
from .models import User
class UserSerialzer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username','password']
        extra_kwargs={
            'password':{'write_only':True}
        }
        
# urls.py      
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('users',UserView,'users')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/',include(router.urls))
]