【请求限制】
# 1 Request类的对象
-每个请求一个新的request---》http请求所有的东西-》地址,请求方法,请求参数,请求体,客户端ip。。。
-新的了 drf提供的
# 2 继承APIView后---》请求可以是urlencoded,form-data,json格式--》都能提交到后端
-request.data 都能取出请求体的数据---》字典(QueryDict)
# 3 控制解析格式使用
from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.parsers import JSONParser, FormParser, MultiPartParser class RequestView(APIView): # 限制请求,只接受json格式 # 方式一:在视图类中控制 parser_classes = [JSONParser] # 用其他格式不被允许 def get(self, request): print(request.data) # form-data格式携带文件和数据,数据在request.data中,文件也在request.data中,同时文件也在request.FILES中,但是另外两种格式文件都没有数据 # print(request.FILES) return Response({"post-ok"})
------------------------------------------------------------------------------
方式二:在配置文件里配置
# 限制请求:所有drf的配置都在这里面---------字典,全局生效,如果局部再控制,在局部再写方式一即可
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
],
}
【响应】
1.1(响应对象)
Response参数分析
PS:
补充:报django-session表不存在
-前端带了 sessionid---》到后端,它就会django-session中取数据,取不到就报错了
1.2(
响应格式)
# =======================================================响应格式 class RequestView(APIView): parser_classes = [JSONParser, FormParser, MultiPartParser] # 响应方式一:在视图类中配置 renderer_classes = [JSONRenderer] #一般默认使用这个 def post(self, request): print(request.data) return Response({"post-ok"}) def get(self, request): res = Response( data=None, status=HTTP_201_CREATED, headers={'name': 'jh'}, ) print(res.status_text) return res
--------------------------------------------------------------
响应方式二:在配置文件中配置
REST_FRAMEWORK = {
# 响应格式
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
# 'rest_framework.renderers.TemplateHTMLRenderer',
],
}
。
。
。
。
。
【两个视图基类】
APIView和GenericAPIView
1 # GenericAPIView+序列化类+Response(继承了APIView),写5个接口 2 from rest_framework.generics import GenericAPIView 3 4 5 class BookView(GenericAPIView): 6 # 两属性 7 queryset = Book.objects.all() # 查询所有的数据 8 serializer_class = BookSerializer # 序列化类 9 10 def get(self, request): 11 # 查询所有 12 obj_list = self.get_queryset() # 使用get_queryset方法获取所有数据,不要使用self.queryset获取属性 13 # 序列化后的对象 14 ser = self.get_serializer(instance=obj_list, many=True) # 使用序列化类,直接使用方法:get_serializer 15 return Response(ser.data) 16 17 def post(self, request): 18 # 新增 19 ser = self.get_serializer(data=request.data) 20 if ser.is_valid(): 21 ser.save() 22 return Response('成功') 23 else: 24 return Response(ser.errors) 25 26 27 class BookDetailView(GenericAPIView): 28 queryset = Book.objects.all() # 查询所有的数据 29 serializer_class = BookSerializer # 序列化类 30 31 def get(self, request, pk): 32 # 查询单个 33 # obj = self.get_queryset().filter(pk=pk).first() 34 obj = self.get_object() # 这样写拿单条,不需要传pk 35 ser = self.get_serializer(instance=obj) 36 return Response(ser.data) 37 38 def put(self, request, pk): 39 # 修改 40 obj = self.get_object() 41 ser = self.get_serializer(instance=obj, data=request.data) 42 if ser.is_valid(): 43 ser.save() # 因为序列化器使用了ModelSerializer,所以会自动调用save方法,不需要重写update方法 44 return Response('成功') 45 else: 46 return Response(ser.errors) 47 48 def delete(self, request, pk): 49 # 删除 50 obj = self.get_object() 51 obj.delete() 52 return Response('删除成功')
===================================================
总结:
所有代码不变,只需要改两个类属性即可
两个类属性:queryset和serializer_class
三个方法:self.get_object(获取单个,不需要传pk=pk) self.get_serializer(序列化) self.get_queryset(获取所有)
====================================================================
了解:
lookup_field='id' 用get_object,没有传参数,就是因为它
filter_backends 过滤
pagination_class 分页
。
。
。
。
。
【五个视图扩展类】
# 上述方法还是有需要重复写的代码,那还有什么更好的方法嘛?
1 # ==============================5个视图扩展类+GenericAPIView+序列化类 2 3 from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, \ 4 DestroyModelMixin 5 6 7 # CreateModelMixin # 新增 8 # RetrieveModelMixin # 查询一条 9 # DestroyModelMixin # 删除 10 # ListModelMixin # 查询所有 11 # UpdateModelMixin # 更新一条 12 class BookView(GenericAPIView, CreateModelMixin, ListModelMixin): 13 # 两属性 14 queryset = Book.objects.all() # 查询所有的数据 15 serializer_class = BookSerializer # 序列化类 16 17 def get(self, request): 18 # 查询所有 19 return super().list(request) # 原理看源码 20 21 def post(self, request): 22 # 新增 23 ''' 24 源码: 25 serializer = self.get_serializer(data=request.data) 26 serializer.is_valid(raise_exception=True) 27 self.perform_create(serializer) 28 headers = self.get_success_headers(serializer.data) 29 return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) 30 :param request: 31 :return: 32 ''' 33 return super().create(request) 34 35 36 class BookDetailView(GenericAPIView,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin): 37 queryset = Book.objects.all() # 查询所有的数据 38 serializer_class = BookSerializer # 序列化类 39 40 def get(self, request, pk): 41 # 查询单个 42 return super().retrieve(request, pk) 43 44 def put(self, request, *args,**kwargs): 45 # 修改 46 return super().update(request, *args,**kwargs) 47 48 def delete(self, request, pk): 49 # 删除 50 return super().destroy(request, pk)
【九个视图子类】
。
。
。
。
。
上述还是有重复代码,继续优化
==================9个视图子类 CreateAPIView ListAPIView UpdateAPIView RetrieveAPIView DestroyAPIView ListCreateAPIView # 查所有和新增 RetrieveUpdateDestroyAPIView # 查单个,修改,删除 RetrieveDestroyAPIView # 查单个,删除 RetrieveUpdateAPIView # 查单个和修改
PS:
# 限制只能发送get 请求
# 方式一:
http_method_names = ['get']
。
。
。
。
【视图集】
(定制查询一条的返回格式)
(ModelViewSet源码分析)
# 1 视图类:继承了APIView----》GenericAPIView # 2 有5个方法---》继承了5个视图扩展类: CreateModelMixin RetrieveModelMixin DestroyModelMixin ListModelMixin UpdateModelMixin # 3 写没写 get put post delete--》使用映射 get---》list get---》retrieve put---》update delete--》destroy post-->create # 4 继承了5个视图扩展类和 GenericViewSet 【不是GenericAPIView】 # 5 GenericViewSet:ViewSetMixin+GenericAPIView # 6 ViewSetMixin 核心---》只要继承它--》路由写法就变了--》必须加action--》 -action是请求方式和视图类中方法的映射关系 # 7 以后只要继承ViewSetMixin的视图类 1 as_view 必须加action做映射 2 视图类中,可以写任意名的方法,只要做好映射,就能执行 # 8 ViewSetMixin源码分析--》通过重写as_view使得路由写法变了 @classonlymethod def as_view(cls, actions=None, **initkwargs): # 0 跟APIView的as_view差不多 # 1 actions must not be empty,如果为空抛异常 # 2 通过反射把请求方式同名的方法放到了视图类中--》对应咱们的映射 def view(request, *args, **kwargs): self = cls(**initkwargs) # actions 咱们传入的字典--》映射关系 # {'get': 'list', 'post': 'create'} for method, action in actions.items(): # method=get action=list # method=post action=create # 视图类对象中反射:list 字符串--》返回了 list方法 # handler就是list方法 handler = getattr(self, action) # 把handler:list方法 通过反射--》放到了视图类的对象中、 # method:get # 视图类的对象中有个get--》本质是list setattr(self, method, handler) return self.dispatch(request, *args, **kwargs) # APIView的dispatch return csrf_exempt(view)
(viewsets下常用的类)
# 1 ModelViewSet --》视图类继承它---》5个接口都有了
# 2 ViewSetMixin--》视图类继承他---》路由写法变了--》映射的
-它必须在视图类前面
# 3 APIView+ViewSetMixin=ViewSet--》路由写法变了--》映射
post请求---》执行login
# 4 GenericAPIView+ViewSetMixin=GenericViewSet--》路由写法变了--》映射
post请求---》执行register
# 5 ReadOnlyModelViewSet--》视图类继承它---》2个接口都有了
查所有
查单条
PS补充部分:
通过ReadOnlyModelViewSet编写2个只读接口
1 # 路由 2 urlpatterns = [ 3 path('books/', views.BookView.as_view({'get': 'list'})), 4 path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve'})), 5 ] 6 7 # 视图类 8 class BookView(ReadOnlyModelViewSet): # 查询所有,新增一个 9 queryset = Book.objects.all() 10 serializer_class = BookSerializer 11 12 --------------------------- 13 # ReadOnlyModelViewSet继承了查询单个与查询所有的两个视图扩展类 和 GenericViewSet
。
。
ViewSetMixin源码分析
ModelViewSet 继承了全部5个视图扩展类 和 GenericViewSet
GenericViewSet 继承了ViewSetMixin 和 GenericAPIView
这里面只有ViewSetMixin没见过,继承的其他几个类前面用过,都不需要改变路由的写法
所以ViewSetMixin类是导致路由写法变了的原因
【视图层大总结】
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------学了视图集以后,那我们该如何选择视图类呢?
#1 APIView -如果后续,写个接口,不需要用序列化类,不跟数据库打交道 -发送短信接口,发送邮件接口 #2 GenericAPIView -如果后续,写个接口,要做序列化或跟数据库打交道,就要继承他 -登陆接口,注册接口 #3 5个视图扩展类,必须配合 GenericAPIView -写5个接口或之一 -class PublishView(GenericAPIView,ListModelMixin) queryset serializer_class def get(request): return super().list(request) # 4 如果要写5个接口之一或之几,直接使用9个视图子类 -新增 -class BookView(CreateAPIView): queryset serializer_class # 5 ViewSet: 原来继承APIView,但是想自动生成路由或路由映射,就继承他 -send_sms # 6 GenericViewSet:原来继承GenericAPIView,但是想自动生成路由或路由映射,就继承他 -login -register # 7 ModelViewSet-->5个接口都写,自动生成路由 # 8 扩展写法 class BooView(GenericViewSet,ListModelMixin): class BooView(ViewSetMixin,GenericAPIView,ListModelMixin): class BooView(ViewSetMixin,ListAPIView): queryset serializer_class def login():
PS:断言
# 源码中经常见到断言 # 语法: assert 条件,'字符串' # 翻译成if if 条件: 条件成立,继续走后续代码 else: raise Exception('字符串') # 案例 assert isinstance(a,int),'不行,a必须是int' # 源码中 assert isinstance(request, HttpRequest), ( 'The `request` argument must be an instance of ' '`django.http.HttpRequest`, not `{}.{}`.' .format(request.__class__.__module__, request.__class__.__name__) )
。
。
。
【路由】
1.自动生成路由:SimpleRouter,DefaultRouter
第一步: 导入路由类 from rest_framework.routers import SimpleRouter,DefaultRouter 第二步:实例化得到对象(两个类,一般使用SimpleRouter) router = SimpleRouter() 第三步: 注册路由(就是让路径与视图类做绑定关系,有几个视图类,就要建立几个关系) router.register('books', views.BookView, 'books') # views.BookView 视图类就会自动生成books这个路径的路由,或者说访问books路由就会自动 # 触发views.BookView里面的接口函数 router.register('publish', views.PublishView, 'publish') ''' 第一个参数是路径,不要带/ 第二个参数是视图类 第三个参数是别名,一般和路径相同 ''' ------------------------ 第四步:在urlpatterns中注册,两种方式: # 方式一 urlpatterns += router.urls # router.urls会自动生成路由 # 方式二 from django.urls import path,include urlpatterns = [ path('', include(router.urls)) # 生成的路由路径 books/ path('api/v1/', include(router.urls)) # include的好处就是可以加路由前缀 # 路由路径 api/v1/books/ ] # 注意router.urls自动生成路由时,会根据视图类所继承的类的不同,所自动生成的路由是有区别 # 比如如果视图类继承的是(ViewSetMixin,ListAPIView)自动生成的路由就是books/ # 但是如果视图类继承的是(ViewSetMixin,RetrieveAPIView)自动生成的路由就是books/再加一个匹配到的数字 # 本质是因为ListAPIView与RetrieveAPIView视图子类里面的方法不一样,一个是list方法一个是retrieve print(router.urls) [ <URLPattern '^books/$' [name='books-list']>, <URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']> ] ------------------------ # 底层实现: -自动生成路由本质是自动做映射,能够自动成的前提是, -视图类中要有5个方法(list...)的某一个或某多个!!! get--->list get---->retrieve put---->update post---->create delete---->destory -ModelViewSet,ReadOnlyModelViewSet 可以自动生成路由 -9个试图子类+必须配合ViewSetMixin 才可以自动生成路由 -GenericAPIView+5个试图扩展类+ 也要配合ViewSetMixin 才能自动生成路由 总结就是一句话,必须要继承ViewSetMixin 才能自动生成路由 ------------------------------ 视图类的继承中,继承9个试图子类+ViewSetMixin 该方法用的最多!!!
.----------------------------------------------------------------------------
SimpleRouter与DefaultRouter区别,了解即可
多几条路由:都一样,知识有个api-root 不太一样
以后注册多了:router.register('books',BookView,'books')
能看到:{"books":"http://127.0.0.1:8000/app01/books/"}