一:APIView和View的区别
a:rest-framework对原Request进行了封装
--request.query_params 存放的是我们get请求的参数
--request.data 存放的是我们所有的数据,包括post请求的以及put,patch请求
二:对Views视图进行封装
a:观察几个请求对应的方法的共同之处
1:models.Book.objects.all()或者models.Book.objects.filter(**kwargs)
2:book_serializers = BookSerializers(book_obj,data=request.data,partial=True) #参数不一样
def put(self,request,id): print(request.data) book_obj = Book.objects.filter(id=id).first() # partial True 表示可以进行部分字段跟新,Flase 表示全部跟新,默认False book_serializers = BookSerializers(book_obj,data=request.data,partial=True) #需要重写 updata if book_serializers.is_valid(): book_serializers.save() return Response(book_serializers.validated_data) else: return Response(book_serializers.errors) def delete(self,request,id): book_obj = Book.objects.filter(id=id).first() book_obj.delete() return Response("")
3:进行初次封装
注意点:
i:定义序列化和反序列化类 并进行设置 serializer_class = BookSerializers
II:获取到对应的对象(可以时多个也可以是单个)query_set = models.Book.objects.all()
III:创建一个类 GenericApIview 将上面两个属性进行通过方法获取
IV:Mixin类对不同的方法进行封装。并因为里面没有 query_set和serializer_class属性。所以需要和GenericApiView类进行混合继承。
from SerDemo import models from rest_framework.views import APIView #视图函数继承APIView 不是View from rest_framework.response import Response #用于返回值 #BookSerializers 自己定义的序列化规则 from SerDemo.serializers import * class GenericAPIView(APIView): query_set = None serializer_class = None def get_query_set(self): return self.query_set def get_serializer_class(self,*args,**kwargs): return self.serializer_class(*args,**kwargs) class ListModelMixin(object): def list(self, request, *args, **kwargs): book_obj = self.get_query_set() # book_obj = models.Book.objects.all() # 序列化多组数据 和一个对象,需要many=TRUE 默认一个 ret = self.get_serializer_class(book_obj, many=True) # ret =BookSerializers(book_obj,many=True) # 序列化的数据在data属性中 return Response(ret.data) class CreateModelMixin(object): def create(self,request,*args,**kwargs): data = self.get_serializer_class(data=request.data) # 进行数据验证,并且在BookSerializers中重写create方法 if data.is_valid(): data.save() return Response(data.data) else: return Response(data.errors) class RetrieveModelMixin(object): def retrieve(self,id,request,*args,**kwargs): book_obj = self.get_query_set().filter(id=id).first() book = self.get_serializer_class(book_obj) return Response(book.data) class UpdateModelMixin(object): def update(self, request, id, *args, **kwargs): book_obj = self.get_query_set().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_query_set() try: queryset.get(pk=id).delete() return Response("") except Exception as e: return Response("信息有误") class BookApiView(GenericAPIView,ListModelMixin,CreateModelMixin): query_set=models.Book.objects.all() serializer_class=BookSerializers def get(self,request,*args,**kwargs): # book_obj =self.get_query_set() # book_obj = models.Book.objects.all() #序列化多组数据 和一个对象,需要many=TRUE 默认一个 # ret = self.get_serializer_class(book_obj,many=True) # ret =BookSerializers(book_obj,many=True) #序列化的数据在data属性中 return self.list(request, *args, **kwargs) def post(self,request,*args,**kwargs): return self.create(request,*args,**kwargs) class BookEditView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): query_set = models.Book.objects.all() serializer_class = BookSerializers def get(self, request,id,*args,**kwargs): return self.retrive(id,request,*args,**kwargs) def put(self,request,id,*args,**kwargs): return self.update(request,id,*args,**kwargs) def delete(self,request,id,*args,**kwargs): return self.destroy(request, id, *args, **kwargs)
4:再次优化封装
现在有两个问题
一:view中有两个方法。能不能合成一个
二:对应的View视图函数中只有对应的方法调用,不存在任何逻辑。能否通过url映射取出对应的方法
a:修改url
--观察上面修改的方法,可以看出link/ 路径下 get ---调用类了 self.list post调用了 self.create方法
--可以得出结论 如果我们按下面袖该的url分发并执行对应的函数,那么我们就可以进一步简化
--但是原有的方法as_view方法不能进行传参数
urlpatterns = [ path('link/', views.BookModelSetView.as_view({"get":"list","post":"create"})), path('retrieve/<int:id>', views.BookModelSetView.as_view({"get":"retrieve","put":"update","delete":"destroy"})) ]
b:解决as_view()方法传参
i:导入ViewSetMixin类
from rest_framework.viewsets import ViewSetMixin
II:阅读对应的源码:
--as_view()进行重写,然后看view方法(不建议改动源码,我是为了学习注释。)
@classonlymethod def as_view(cls, actions=None, **initkwargs): """ Because of the way class based views create a closure around the instantiated view, we need to totally reimplement `.as_view`, and slightly modify the view function that is created and returned. """ # The name and description initkwargs may be explicitly overridden for # certain route confiugurations. eg, names of extra actions. cls.name = None cls.description = None # The suffix initkwarg is reserved for displaying the viewset type. # This initkwarg should have no effect if the name is provided. # eg. 'List' or 'Instance'. cls.suffix = None # The detail initkwarg is reserved for introspecting the viewset type. cls.detail = None # Setting a basename allows a view to reverse its action urls. This # value is provided by the router through the initkwargs. cls.basename = None # actions must not be empty if not actions: raise TypeError("The `actions` argument must be provided when " "calling `.as_view()` on a ViewSet. For example " "`.as_view({'get': 'list'})`") # sanitize keyword arguments for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r" % ( cls.__name__, key)) # name and suffix are mutually exclusive if 'name' in initkwargs and 'suffix' in initkwargs: raise TypeError("%s() received both `name` and `suffix`, which are " "mutually exclusive arguments." % (cls.__name__)) def view(request, *args, **kwargs): self = cls(**initkwargs) # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. self.action_map = actions # Bind methods to actions # This is the bit that's different to a standard view ''' 1:actions={"get":"list","post":"create"} as_view()方法中传递的参数 2:循环后结果为 methon ="get" #key action ='list' #对应的方法字符串 ''' for method, action in actions.items(): ''' 3:通过getattr将字符串反射对应的方法名 handler = list #循环将每一个字符传,修改对应的放啊 4:通过setattr(self, method, handler) 得到的结果为 set.method =self.handler -->self.get=self.list. 5:成功映射对应的方法 ''' handler = getattr(self, action) setattr(self, method, handler) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs # And continue as usual ''' 6:重新进行路由分发,并且已经修改成功方法 ''' return self.dispatch(request, *args, **kwargs) # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) # We need to set these on the view function, so that breadcrumb # generation can pick out these bits of information from a # resolved URL. view.cls = cls view.initkwargs = initkwargs view.actions = actions return csrf_exempt(view)
c:最后进行view编写:
from rest_framework.viewsets import ViewSetMixin # 定义一个类,用来简化BookModelSetView的继承类的长度,不是很重要 class ModelViewSet(ViewSetMixin,GenericAPIView,RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin,CreateModelMixin,ListModelMixin): pass class BookModelSetView(ModelViewSet): query_set = models.Book.objects.all() serializer_class = BookSerializers
5:使用rest_framework自带的方法实现以上功能
--导入 from rest_framework.viewsets import ModelViewSet
--参数是queryset和serializer_class
--路由修改id 成pk
--该方法和上面自修改方法本质上相同。
--使用自带的方法简便,但是有时候并不需要那么多的方法,所以自定义方法可以由于简化请求方法。
from rest_framework.viewsets import ModelViewSet class BookModelSetView(ModelViewSet): queryset = models.Book.objects.all() serializer_class = BookSerializers
urlpatterns = [ path('link/', views.BookModelSetView.as_view({"get":"list","post":"create"})), path('retrieve/<int:pk>', views.BookModelSetView.as_view({"get":"retrive","put":"update","delect":"destroy"})) ]
三:小结
1:以上用到的方法均在以下4个类中,可以查看下源码
from rest_framework import views,generics,mixins,viewsets
四:路由系统
了解下即可,最好不要用这种方法
from .views import BookView from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(r"book", BookView) urlpatterns = [ ] urlpatterns += router.urls