Django的CBV FBV
(function base views) 就是在视图里使用函数处理请求.
CBV
(class base views) 就是在视图里使用类处理请求.
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态).所以
Django在后来加入了Class-Based—View.可以让我们用类写View.
这样做的优点主要下面两种:
提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承) 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性 CBV简单示例 我们简单来看下如何使用CBV模式,然后再分析下源代码是如何执行的,源码之后在讲解
在urls.py
中进行路由配置
1 2 3 urlpatterns = [ url(r'^login/$' ,views.LoginView.as_view()), ]
views
视图中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from django.shortcuts import render,HttpResponse,redirectfrom django.views import Viewclass LoginView (View ): def get (self,request ): print ("GET" ) return render(request,"login.html" ) def post (self,request ): print ("POST" ) user=request.POST.get("user" ) pwd=request.POST.get("pwd" ) return HttpResponse("OK" )
进行测试http://localhost:8000/login/
源码分析 我们分析源码从路由配置开始,
1 2 3 4 5 6 7 8 9 10 11 from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [ url(r'^admin/' , admin.site.urls), url(r'^login/' , views.LoginView.as_view()), ]
这里的url配置我们可以看出,浏览器访问index
会执行它对应的函数views.LoginView.as_view()
,那么views.LoginView.as_view()
又干了什么呢?
首先我们去views.LoginView找要执行的as_view(),你会发现找不到,我们接着去他的父类View里边找找到了,我们把代码贴出来分析一下, 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 @classonlymethod def as_view (cls, **initkwargs ): """ 请求-响应过程的主要入口点。 """ 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. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view (request, *args, **kwargs ): self = cls(**initkwargs) if hasattr (self, 'get' ) and not hasattr (self, 'head' ): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs update_wrapper(view, cls, updated=()) update_wrapper(view, cls.dispatch, assigned=()) return view
由于我们的配置中views.IndexView.as_view()参数为null
,所以在for key in initkwargs
会直接跳过,如果不为null
,就去判断执行接下来的代码,大概意思就是,if key in cls.http_method_names:
和if not hasattr(cls, key):
,如果传递过来的字典中某键包含在 http_method_names
列表中和本类中没有该键属性都会跑出异常,http_method_names
列表如下
根据上边刨析,我们可以得出
1 2 3 4 5 6 url(r'^login/' , views.LoginView.as_view()), url(r'^login/' , Views.as_view()), url(r'^login/' , Views.view()),
用户一旦访问login—那么Views.view(request)就会被调用执行.只要视图函数被调用,url会自动传入一个request
那我们现在来view函数干了什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def view (request, *args, **kwargs ): self = cls(**initkwargs) if hasattr (self, 'get' ) and not hasattr (self, 'head' ): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs update_wrapper(view, cls, updated=()) update_wrapper(view, cls.dispatch, assigned=()) return view
经过上边我们可以得出的结论
这里的执行结果返回什么,用户看到的就是什么
1 2 view(request): return self.dispatch(request, *args, **kwargs)
现在我们就要确定的是self是谁?
经上图得出的结论:
self.dispatch 会先从自己(LoginView)类里边找dispatch
self.dispatch加()了,所以这是一个实例方法的调用执行
所以 dispatch 返回什么view()就返回什么
1 2 view(request): return self.dispatch(request, *args, **kwargs)
LoginView类里自己有dispatch的话就调用自己的,没有的话就去自己的父类里边找;
我们在LoginView类里边没找到dispatch,所以还得去他的父类里边找dispatch
以下是View类里的dispatch函数
1 2 3 4 5 6 7 8 9 10 11 12 def dispatch (self, request, *args, **kwargs ): if request.method.lower() in self.http_method_names: handler = getattr (self, request.method.lower(), self.http_method_not_allowed) else : handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
dispatch就是分发不同的请求方法走对应的函数,所以自己的视图函数写不同方法执行后返回的结果
view
视图中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from django.shortcuts import render,HttpResponse,redirectfrom django.views import Viewclass LoginView (View ): def get (self,request ): print ("GET" ) return render(request,"login.html" ) def post (self,request ): print ("POST" ) user=request.POST.get("user" ) pwd=request.POST.get("pwd" ) return HttpResponse("OK" )
总结:
as_view()返回的结果就是—> view()返回的结果就是—> dispatch()返回的结果就是—>handler()返回的结果—> 自己定义请求方法函数的返回结果,否则就抛错405
self是调用as_view()类的实例化
类的查找:不管什么时候调用什么先去自己类里面找,没有就去自己的父类里面找
405 - 用来访问本页面的 HTTP 谓词不被允许(方法不被允许)
Django_Rest_Framework 核心思想: 大量缩减编写api接口的代码
Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。REST framework提供了一个API 的Web可视化界面来方便查看测试接口。
中文文档:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework
github: https://github.com/encode/django-rest-framework/tree/master
特点:
提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化; 提供了丰富的类视图、Mixin扩展类,简化视图的编写; 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要; 多种身份认证和权限认证方式的支持;[jwt] 内置了限流系统; 直观的 API web 界面;【方便我们调试开发api接口】 可扩展性,插件丰富 安装 DRF需要以下依赖:
Python (3.5 以上) Django (2.2 以上) DRF是以Django子应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。(若没有Django环境,需要先创建环境安装Django)
前提是已经安装了django,建议安装在虚拟环境
1 2 3 4 # pip install -i https://pypi.tuna.tsinghua.edu.cn/simple djangorestframework # pip3 install djangorestframework
配置 settings.py注册app
1 2 3 4 5 6 INSTALLED_APPS = [ ........ 'rest_framework' , ]
APIView使用 1 rest_framework.views.APIView
APIView
是REST framework提供的所有视图的基类,继承自Django的View
父类。
相较于CBV,路由不变,视图不变,继承类改为APIView
新建一个app
1 python manage.py startapp app
app/views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from rest_framework.views import APIViewfrom rest_framework.response import Responseuser_list = [{'id' : 1 , 'name' : 'Bob' }, {'id' : 2 , 'name' : 'Tom' }] class Users (APIView ): def get (self, request, *args, **kwargs ): return Response({ 'status' : 0 , 'msg' : 'ok' , 'results' : user_list }) def post (self, request, *args, **kwargs ): name = request.data.get('name' ) id = len (user_list) + 1 user = {'id' : id , 'name' : name} user_list.append(user) return Response({ 'status' : '0' , 'msg' : 'ok' , 'results' : user })
主路由
1 2 3 4 5 from app import viewsurlpatterns = [ url(r'^users/' , views.Users.as_view()), ]
访问[Users – Django REST framework](http://127.0.0.1:8000/users/)
返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "status" : 0 , "msg" : "ok" , "results" : [ { "id" : 1 , "name" : "Bob" } , { "id" : 2 , "name" : "Tom" } ] }
APIView源码分析 前言:APIView基于View 看这部分内容一定要懂CBV源码分析里的内容
在CBV源码分析中,我们是分析的from django.views import View下的执行流程
这篇博客我们就来了解下APIView是如何执行的,跟django.views模块下的view有何关联?
我们依然从urls.py
配置入手分析
1 2 3 4 5 6 7 from django.conf.urls import urlfrom django.contrib import adminfrom app import viewsurlpatterns = [ url(r'^users/' , views.Users.as_view()), ]
views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from rest_framework.views import APIViewfrom rest_framework.response import Responseuser_list = [{'id' : 1 , 'name' : 'Bob' }, {'id' : 2 , 'name' : 'Tom' }] class Users (APIView ): def get (self, request, *args, **kwargs ): return Response({ 'status' : 0 , 'msg' : 'ok' , 'results' : user_list }) def post (self, request, *args, **kwargs ): name = request.data.get('name' ) id = len (user_list) + 1 user = {'id' : id , 'name' : name} user_list.append(user) return Response({ 'status' : '0' , 'msg' : 'ok' , 'results' : user })
1、首先我们还是来确认urls.py
中as_view
是谁执行的?
首先我们去views.PublishView
中找,发现找不到,所以我们接着再去PublishView
的父类APIView
中去找,找到了所以
执行调用APIView.as_view()
,内容如下:
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 class APIView (View ): @classmethod def as_view (cls, **initkwargs ): if isinstance (getattr (cls, 'queryset' , None ), models.query.QuerySet): def force_evaluation (): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation view = super (APIView, cls).as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs return csrf_exempt(view) def dispatch (self, request ): request = self.initialize_request(request, *args, **kwargs) self.request = request self.initial(request, *args, **kwargs) handler = getattr (self, request.method.lower(),self.http_method_not_allowed) return handler(request, *args, **kwargs)
2、大家是不是以为这样就结束了?NO!NO!NO!
如果是as_view是View的as_view
,dispatch是View的dispatch
,那rest-framework
不就成废钞了么?
as_view的执行结果是dispatch的执行结果,那么dispatch还是View的dispatch么?
我们先看下views.PublishView里边有没有dispatch,发现没有 我们在views.PublishView的父类APIView,发现有 所以dispatch是APIView.dispatch非View.dispatch APIView.dispatch 里边有一堆组件,这里不说 中间的这点代码跟之前的View.dispatch一样:
还是做请求分发,在请求之外又做了一堆事情
1 2 3 4 5 6 7 if request.method.lower() in self.http_method_names: handler = getattr (self, request.method.lower(), self.http_method_not_allowed) else : handler = self.http_method_not_allowed response = handler(request, *args, **kwargs)
总结:
url 转变过程
url(r’^publishes/', views.PublishView.as_view()), url(r’^publishes/', APIView.as_view()), url(r’^publishes/', View.as_view()), View.as_view()我们在django—CBV 讲解过了
所以最后调用还是,只是APIView.as_view在里边加了一些他自己定义的一些东西,只是这里没讲到而已
APIView.as_view()—> View.as_view()—> APIView.dispatch()—>response—>handler()–> 自己定义请求方法函数的返回结果,否则就抛错405
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 view = super (APIView, cls).as_view(**initkwargs) return csrf_exempt(view) request = self.initialize_request(request, *args, **kwargs) self.initial(request, *args, **kwargs) self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request)
APIView
与View
的不同之处在于:
传入到视图方法中的是REST framework的Request
对象,而不是Django的HttpRequeset
对象; 视图方法可以返回REST framework的Response
对象,视图会为响应数据设置(render)符合前端期望要求的格式; 任何APIException
异常都会被捕获到,并且处理成合适格式的响应信息返回给客户端; 重新声明了一个新的as_views方法并在dispatch()进行路由分发前,会对请求的客户端进行身份认证、权限检查、流量控制。 DRF在django原有的基础上,新增了一个request对象继承到了APIVIew视图类,并在django原有的HttpResponse响应类的基础上实现了一个子类rest_framework.response.Response响应类。这两个类,都是基于内容协商来完成数据的格式转换的。
请求模块 REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request 类的对象。
REST framework 提供了Parser 解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request 对象中。
Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。
常用属性 request.data
返回解析之后的请求体数据。类似于Django中标准的request.POST
和 request.FILES
属性,但提供如下特性:
包含了解析之后的文件和非文件数据 包含了对POST、PUT、PATCH请求方式解析后的数据 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据 request.query_params
与Django标准的request.GET
相同,只是更换了更正确的名称而已。
request._request
获取django封装的Request对象
基本使用 视图代码
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 from django.views import Viewfrom django.http.response import HttpResponsefrom django.http.request import HttpRequestfrom django.core.handlers.wsgi import WSGIRequestclass ReqView (View ): def get (self,request ): print (request) return HttpResponse("ok" ) """ 默认情况下, 编写视图类时,如果继承的是django内置的django.view.View视图基类, 则视图方法中得到的request对象,是django默认提供的django.core.handlers.wsgi.WSGIRequest WSGIRequest这个请求处理对象,无法直接提供的关于json数据数据处理。 在编写api接口时很不方便,所以drf为了简写这块内容,在原来的HttpRequest的基础上面,新增了一个Request对象 这个Request对象是单独声明的和原来django的HttpRequest不是父子关系。 同时注意: 要使用drf提供的Request请求处理对象,必须在编写视图类时继承drf提供的视图基类 from rest_framework.views import APIView 如果使用drf提供的视图基类APIView编写类视图,则必须使用来自drf提供的Request请求对象和Response响应对象 """ from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework import statusclass ReqAPIView (APIView ): def get (self,request ): print (request) print (request.query_params) print (request.query_params.get("name" )) print (request.query_params.getlist("lve" )) return Response("ok" ) def post (self, request ): print (request.data) """直接从请求体中提取数据转 # 客户端如果上传了json数据,直接返回字典 {'name': '灰太狼', 'age': 20, 'sex': 1, 'classmate': '301', 'description': '我还会再回来的~'} # 客户端如果上传了表单数据,直接返回QueryDict <QueryDict: {'name': ['xiaohui'], 'age': ['18']}> """ print (request.FILES) print (request._request.META.get("Accept" )) response = Response(data="not ok" , status=status.HTTP_400_BAD_REQUEST, headers={"Company" :"Oldboy" }) return response
响应模块 1 rest_framework.response.Response
REST framework提供了一个响应类Response
,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染器)成符合前端需求的类型。
REST framework提供了Renderer
渲染器,用来根据请求头中的Accept
(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用Content-Type方式处理响应数据,我们可以通过配置来修改默认响应格式。
可以在rest_framework.settings 查找所有的drf默认配置项
1 2 3 4 5 6 REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES' : ( 'rest_framework.renderers.JSONRenderer' , 'rest_framework.renderers.BrowsableAPIRenderer' , ) }
构造方式 1 Response(data, status=None , template_name=None , headers=None , content_type=None )
drf的响应处理类和请求处理类不一样,Response就是django的HttpResponse响应处理类的子类。
data
数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用renderer
渲染器处理data
。
data
不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer
序列化器序列化处理后(转为了Python字典类型)再传递给data
参数。
参数说明:
data
: 为响应准备的序列化处理后的数据;status
: 状态码,默认200;template_name
: 模板名称,如果使用HTMLRenderer
时需指明;headers
: 用于存放响应头信息的字典;content_type
: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数response对象的属性 .data:传给response对象的序列化后,但尚未render处理的数据
.status_code:状态码的数字
.content:经过render处理后的响应数据
状态码 为了方便设置状态码,REST framewrok在rest_framework.status
模块中提供了常用http状态码的常量。
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 HTTP_100_CONTINUE HTTP_101_SWITCHING_PROTOCOLS HTTP_200_OK HTTP_201_CREATED HTTP_202_ACCEPTED HTTP_203_NON_AUTHORITATIVE_INFORMATION HTTP_204_NO_CONTENT HTTP_205_RESET_CONTENT HTTP_206_PARTIAL_CONTENT HTTP_207_MULTI_STATUS HTTP_300_MULTIPLE_CHOICES HTTP_301_MOVED_PERMANENTLY HTTP_302_FOUND HTTP_303_SEE_OTHER HTTP_304_NOT_MODIFIED HTTP_305_USE_PROXY HTTP_306_RESERVED HTTP_307_TEMPORARY_REDIRECT HTTP_400_BAD_REQUEST HTTP_401_UNAUTHORIZED HTTP_402_PAYMENT_REQUIRED HTTP_403_FORBIDDEN HTTP_404_NOT_FOUND HTTP_405_METHOD_NOT_ALLOWED HTTP_406_NOT_ACCEPTABLE HTTP_407_PROXY_AUTHENTICATION_REQUIRED HTTP_408_REQUEST_TIMEOUT HTTP_409_CONFLICT HTTP_410_GONE HTTP_411_LENGTH_REQUIRED HTTP_412_PRECONDITION_FAILED HTTP_413_REQUEST_ENTITY_TOO_LARGE HTTP_414_REQUEST_URI_TOO_LONG HTTP_415_UNSUPPORTED_MEDIA_TYPE HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE HTTP_417_EXPECTATION_FAILED HTTP_422_UNPROCESSABLE_ENTITY HTTP_423_LOCKED HTTP_424_FAILED_DEPENDENCY HTTP_428_PRECONDITION_REQUIRED HTTP_429_TOO_MANY_REQUESTS HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS HTTP_500_INTERNAL_SERVER_ERROR HTTP_501_NOT_IMPLEMENTED HTTP_502_BAD_GATEWAY HTTP_503_SERVICE_UNAVAILABLE HTTP_504_GATEWAY_TIMEOUT HTTP_505_HTTP_VERSION_NOT_SUPPORTED HTTP_507_INSUFFICIENT_STORAGE HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
解析模块 1、解析器的作用 根据请求头 content-type 选择对应的解析器对请求体内容进行处理。
有application/json,x-www-form-urlencoded,form-data等格式
2、全局使用解析器 1 2 3 4 5 6 7 8 REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES' :[ 'rest_framework.parsers.JSONParser' 'rest_framework.parsers.FormParser' 'rest_framework.parsers.MultiPartParser' ] }
1 2 3 urlpatterns = [ url(r'test/' , TestView.as_view()), ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from rest_framework.views import APIViewfrom rest_framework.response import Response class TestView (APIView ): def post (self, request, *args, **kwargs ): print (request.content_type) print (request.data) print (request.POST) print (request.FILES) return Response('POST请求,响应内容' ) def put (self, request, *args, **kwargs ): return Response('PUT请求,响应内容' )
3、局部使用解析器 3-1 仅处理请求头content-type为application/json的请求体
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 from django.conf.urls import url, includefrom web.views.s5_parser import TestView urlpatterns = [ url(r'test/' , TestView.as_view(), name='test' ), ] from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.request import Requestfrom rest_framework.parsers import JSONParser class TestView (APIView ): parser_classes = [JSONParser, ] def post (self, request, *args, **kwargs ): print (request.content_type) print (request.data) print (request.POST) print (request.FILES) return Response('POST请求,响应内容' ) def put (self, request, *args, **kwargs ): return Response('PUT请求,响应内容' )
3-2 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体
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 from django.conf.urls import url, includefrom web.views import TestView urlpatterns = [ url(r'test/' , TestView.as_view(), name='test' ), ] from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.request import Requestfrom rest_framework.parsers import FormParser class TestView (APIView ): parser_classes = [FormParser, ] def post (self, request, *args, **kwargs ): print (request.content_type) print (request.data) print (request.POST) print (request.FILES) return Response('POST请求,响应内容' ) def put (self, request, *args, **kwargs ): return Response('PUT请求,响应内容' )
3-3 仅处理请求头content-type为multipart/form-data的请求体
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 from django.conf.urls import url, includefrom web.views import TestView urlpatterns = [ url(r'test/' , TestView.as_view(), name='test' ), ] from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.request import Requestfrom rest_framework.parsers import MultiPartParser class TestView (APIView ): parser_classes = [MultiPartParser, ] def post (self, request, *args, **kwargs ): print (request.content_type) print (request.data) print (request.POST) print (request.FILES) return Response('POST请求,响应内容' ) def put (self, request, *args, **kwargs ): return Response('PUT请求,响应内容' ) <!DOCTYPE html> <html lang="en" > <head> <title>Title</title> </head> <body> <form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data" > <input type ="text" name="user" /> <input type ="file" name="img" > <input type ="submit" value="提交" > </form> </body> </html>
3-4 仅上传文件
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 from django.conf.urls import url, includefrom web.views import TestView urlpatterns = [ url(r'test/(?P<filename>[^/]+)' , TestView.as_view(), name='test' ), ] from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.request import Requestfrom rest_framework.parsers import FileUploadParser class TestView (APIView ): parser_classes = [FileUploadParser, ] def post (self, request, filename, *args, **kwargs ): print (filename) print (request.content_type) print (request.data) print (request.POST) print (request.FILES) return Response('POST请求,响应内容' ) def put (self, request, *args, **kwargs ): return Response('PUT请求,响应内容' ) <!DOCTYPE html> <html lang="en" > <head> <title>Title</title> </head> <body> <form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data" > <input type ="text" name="user" /> <input type ="file" name="img" > <input type ="submit" value="提交" > </form> </body> </html>
3-5 同时多个Parser
当同时使用多个parser时,rest framework会根据请求头content-type自动进行比对,并使用对应parser
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 from django.conf.urls import url, includefrom web.views import TestView urlpatterns = [ url(r'test/' , TestView.as_view(), name='test' ), ] from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.request import Requestfrom rest_framework.parsers import JSONParser, FormParser, MultiPartParser class TestView (APIView ): parser_classes = [JSONParser, FormParser, MultiPartParser, ] def post (self, request, *args, **kwargs ): print (request.content_type) print (request.data) print (request.POST) print (request.FILES) return Response('POST请求,响应内容' ) def put (self, request, *args, **kwargs ): return Response('PUT请求,响应内容' )
4、源码分析 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 @property def data (self ): if not _hasattr (self, '_full_data' ): self._load_data_and_files() return self._full_data def _parse (self ): media_type = self.content_type parser = self.negotiator.select_parser(self, self.parsers) def select_parser (self, request, parsers ): for parser in parsers: if media_type_matches(parser.media_type, request.content_type): return parser return None Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) def get_parsers (self ): return [parser() for parser in self.parser_classes] parser_classes = api_settings.DEFAULT_PARSER_CLASSES def __getattr__ (self, attr ): if attr not in self.defaults: raise AttributeError("Invalid API setting: '%s'" % attr) try : val = self.user_settings[attr] except KeyError: val = self.defaults[attr] if attr in self.import_strings: val = perform_import(val, attr) self._cached_attrs.add(attr) setattr (self, attr, val) return val @property def user_settings (self ): if not hasattr (self, '_user_settings' ): self._user_settings = getattr (settings, 'REST_FRAMEWORK' , {}) return self._user_settings
jQuery
vue/cli 3 配置jQuery:在vue.config.js中配置(没有,手动项目根目录下新建)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const webpack = require ("webpack" ); module .exports = { configureWebpack : { plugins : [ new webpack.ProvidePlugin ({ $ : "jquery" , jQuery : "jquery" , "window.jQuery" : "jquery" , Popper : ["popper.js" , "default" ] }) ] } };
BootStrap
1 >: cnpm install bootstrap@3
vue/cli 3 配置BootStrap:在main.js中配置
1 2 import "bootstrap" import "bootstrap/dist/css/bootstrap.css"