zoukankan      html  css  js  c++  java
  • django rest framemark

    一 内容回顾

     1 开发者模式

      普通开发方式:前后端放在一起开发

      前后端分离:前后端只通过 JSON 来交流,组件化、工程化不需要依赖后端去实现

     2 后端开发:为前端提供url接口,也就是API或者接口的开发。永远返回HttpResponse。

     3 django的FBC和CBV

      FBV也就是:function base view的缩写,也就是视图函数。方法如下:

    # def users(request):
    #     user_list=['alex','oldboy','egon']
    #     return HttpResponse(json.dumps(user_list))
    

       CBV也就是:class base view的缩写,也就是类的视图。方法如下:

        注:写的类需要继承View内置的类的方法。在url中使用:类名.as_view()

      在views.py文件中:

       View的导入:from django.views impost View

    # class StudentView(View):
    # 
    #     def get(self,request,*args,**kwargs):
    #         return HttpResponse('GET')
    # 
    #     def post(self,request,*args,**kwargs):
    #         return HttpResponse('POST')
    # 
    #     def delete(self,request,*args,**kwargs):
    #         return HttpResponse('DELETE')
    # 
    #     def put(self,request,*args,**kwargs):
    #         return HttpResponse('PUT')
    

      在url.py文件中:

    url(r'^student/', views.StudentView.as_view()),
    

     CBV补充:CBV是基于反射实现根据请求方式不同,执行不同的方法。

      原理:也就是执行的顺序是url -> view方法 -> dispatch方法(反射执行其他:GET/POST/DELETE/PUT)

      流程如下:

    class StudentView(View):
        def dispatch(self, request, *args, **kwargs):
            ret=super(StudentView,self).dispatch(request, *args, **kwargs)
            return ret
    
        def get(self,request,*args,**kwargs):
            return HttpResponse('GET')
    
        def post(self,request,*args,**kwargs):
            return HttpResponse("POST")
    
        def put(self,request,*args,**kwargs):
            return HttpResponse("PUST")
    
        def delete(self,request,*args,**kwargs):
            return HttpResponse('DELETE')
    

      继承:多个类的共同功能,为了避免重复写代码,如下:

    class MyBaseView:
        def dispatch(self,request,*args,**kwargs):
            print('before')
            ret=super(MyBaseView,self).dispatch(request,*args,**kwargs)
            print('after')
            return ret
    
    class StudentView(MyBaseView,View):
        def get(self,request,*args,**kwargs):
            print('get方法')
            return HttpResponse('GET')
    
        def put(self, request, *args, **kwargs):
            print('put方法')
            return HttpResponse('PUT')
    
        def post(self, request, *args, **kwargs):
            print('post方法')
            return HttpResponse('POST')
    
        def delete(self, request, *args, **kwargs):
            print('delete方法')
            return HttpResponse('DELETE')
    

     4 列表生成式:

    class Foo:
        def __init__(self):
            pass
    
        def work(self):
            print('this is working')
    
    
    class Bar:
        def __init__(self):
            pass
    
        def ect(self):
            print('this is ecting')
    
    
    objs=[item() for item in [Foo,Bar]]
    # 就相当于如下
    # objs=[]
    # for i in [Foo,Bar]:
    #     objs.append(i())
    
    
    objs[0].work()
    objs[1].ect()
    

      5 面向对象的封装:

      1将共同的方法和属性封装到同一个类下面。

    class File:
    	文件增删改查方法
    					
    Class DB:
    	数据库的方法
    

      2 将数据封装到数据中。实例如下:

    class File:
    	def __init__(self,a1,a2):
    		self.a1 = a1 
    		self.xxx = a2
    	def get:...
    	def delete:...
    	def update:...
    	def add:...
    					
    obj1 = File(123,666)
    obj2 = File(456,999)
    

      6 ps扩展:

    class Request:
        def __init__(self,obj):
            self.obj=obj
            
        
        @property
        def user(self):
            return self.obj.authticate()
    
    class Auth:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        
        def authticate(self):
            return self.name,self.age
    class APIView:
        def dispatch(self):
            return self.f2()
    
        def f2(self):
            a=Auth('alex',33)
            ret=Request(a)
            return ret.user
    
    obj=APIView()
    print(obj.dispatch())
    

     二 中间件和csrf_token

     1 django中csrf_token是怎么实现的:csrf_token是在process_view里面验证的。

     2 解决csrf_token的方法:

      2.1:直接在settings文件中将下面这行代码给注释掉

    'django.middleware.csrf.CsrfViewMiddleware',
    

       2.2:通过csrf_exempt装饰器,将不需要csrf_token的试图函数上加上这个装饰器。process_view方法就会检查试图的cerf_token是否免除。

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt   # 
    def users(request):
        user_list=['alex','oldboy','egon']
        return HttpResponse(json.dumps(user_list))
    

       2.3:类的视图如果需要免除csrf_token的验证,就需要在class类使用装饰器method_decorator。不过必须要加上dispatch这个方法。加在单独的方法上面无效。

       使用方法一:直接加在类里面的dispatch方法上面

    from django.views.decorators.csrf import csrf_exempt
    from django.utils.decorators import method_decorator
    # method_decorator 装饰器使用方法一:
    class StudentView(View):
    
        @method_decorator(csrf_exempt)
        def dispatch(self,request,*args,**kwargs):
            ret=super(StudentView,self).dispatch(request,*args,**kwargs)
            return ret
    
        def get(self,request,*args,**kwargs):
            return HttpResponse('GET')
    
        def post(self,request,*args,**kwargs):
            return HttpResponse('POST')
    
        def put(self,request,*args,**kwargs):
            return HttpResponse('PUT')
    
        def delete(self,request,*args,**kwargs):
            return HttpResponse('DELETE')
    

      使用方法二:在class类上面加上这个装饰器,不过需要多创一个属性值,@method_decorator(csrf_token,name='dispatch')

    # method_decorator 装饰器使用方法二:
    
    from django.views.decorators.csrf import csrf_exempt
    from django.utils.decorators import method_decorator
    @method_decorator(csrf_exempt,name='dispatch')
    class StudentView(View):
        
        def get(self, request, *args, **kwargs):
            return HttpResponse('GET')
    
        def post(self, request, *args, **kwargs):
            return HttpResponse('POST')
    
        def put(self, request, *args, **kwargs):
            return HttpResponse('PUT')
    
        def delete(self, request, *args, **kwargs):
            return HttpResponse('DELETE')
    

       2.4 :csrf_protect装饰器,如果在settings文件中将'django.middleware.csrf.CsrfViewMiddleware',这段代码注释掉了的话需要某个试图函数通过csrf_token的验证,就在这个视图函数上面加上这个装饰器。

    from django.views.decorators.csrf import csrf_exempt
    @csrf_protect # 该函数需认证
    def users(request):
        user_list = ['alex','oldboy']
        return HttpResponse(json.dumps((user_list)))
    

      3 django的中间件:如下

    - process_request
    - process_view
    - process_response
    - process_exception
    - process_render_template
    

        django的中间件做过什么:权限;用户认证。

      process_view做过什么:检查视图是否被免除csrf_token的验证;去请求体的cookie中获取csrf_token

    三 restful规范

     什么是restful:

        REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
        REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
        REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
        所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
        对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
    

     restful是为了根据method的不同做这不同的操作。restful就是一个规范,只为了让我们更好的记住url。

     restful的API的设计有哪些。如下:

      1 API与用户的通信协议,总是使用HTTPs协议

      2 域名 :

        https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)
        https://example.org/api/                        API很简单
    

       3 版本:

        URL,如:https://api.example.com/v1/
        请求头                                                  跨域时,引发发送多次请求
    

       4 路径,视网络上任何东西都是资源,均使用名词表示(可复数)

        https://api.example.com/v1/zoos
        https://api.example.com/v1/animals
        https://api.example.com/v1/employees
    

       5 method

        GET      :从服务器取出资源(一项或多项)
        POST    :在服务器新建一个资源
        PUT      :在服务器更新资源(客户端提供改变后的完整资源)
        PATCH  :在服务器更新资源(客户端提供改变的属性)
        DELETE :从服务器删除资源
    

       6 过滤,通过在url上传参的形式传递搜索条件

        https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
        https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
        https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
        https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
        https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
    

       7 状态码

    200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    204 NO CONTENT - [DELETE]:用户删除数据成功。
    400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

    http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

       8 错误处理,状态码是4xx时,应返回错误信息,error当做key。

    {
        error: "Invalid API key"
    }
    

       9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。

    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档
    

       10 Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

    {"link": {
      "rel":   "collection https://www.example.com/zoos",
      "href":  "https://api.example.com/zoos",
      "title": "List of zoos",
      "type":  "application/vnd.yourformat+json"
    }}
    

       借鉴:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

     四 restful的API面试解答

     问题:请说一下你对restful的认识。

     解答:

    restful的API设计模式有API与用户的通信协议是基于HTTPs协议实现的,通过url传参的形式传递搜索条件的,还有错误处理等等。
    在我没有遇到restful API之前需要记住各种各样的很多的url路径,而且写的代码全是靠着试图函数给实现的,代码看起来不是很高端,并且等等
    RESTful架构,是比较流行的一种互联网软件架构。REST,即Representational State Transfer的缩写。
    说白点就是网站即软件,再白点就是一个服务软件支持http的四种方法:
    GET用来获取资源,POST用来新建资源、更新资源,PUT用来更新资源,DELETE用来删除资源。
    
    在我遇到restful之后,不需要记住那么多的url,可以将共同的方法封装到一个类下面,通过调用在这个类的方
    法来访问url,可以通过这个类调用多个方法。并且API很好的解决了跨域的问题。版本有更新的时候,还可以
    将版本号写在url中来提示使用的版本。还可以自己写访问错误时返回的错误信息。传入参数时还可以通过url来传递参数等等,
    
     
    

     借鉴 :

    RESTful架构,目前是比较流行的一种互联网软件架构。REST,即Representational State Transfer的缩写。
    
    说白点就是网站即软件,再白点就是一个服务软件支持http的四种方法:
    
         GET用来获取资源,POST用来新建资源、更新资源,PUT用来更新资源,DELETE用来删除资源。
    
         并对外提供一个或多个URI,每个URI对应一个资源;客户端通过URI配合上面的方法就可以和服务
    
    段的软件交互。客户端主要是浏览器,使用restful框架的软件对http的支持也为了web应用带来方便。
    
         REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。他的贡献很多,
    
    可以了解一下。本人工作的方向是SDN,也算是比较潮的东东,其中floodlight就用到了restful框架。
    
    开发者为软件开发出一些功能,并提供URI api,用户就可以利用浏览器、curl等工具通过提供的URI
    
    从软件中获得想要的信息或者设置软件的功能。
    
         对于发开者来说,就是提供URI和URI对应的资源,并将他们对应上,类似dicts={'/path?':resource}。
    
    比如重写http GET方法:首先获得客户端请求的url,解析url然后判断其对应的URI,由于URI与应一个资源,
    
    那么url就可以访问这个资源了。具体实现上资源也就是方法或者一个类,要看具体实现了。
    
     
    

     五 认证

     1:有些API需要用户登陆过后,才能访问,有些不需要登陆就能够访问

     2:基本使用认证组件:解决,创建两张表,用户登陆后,获取token,存入到数据库。

     3:认证流程原理:

     4:局部视图使用和全局使用,匿名用户request.user=None

     5:内置认证类:

      认证类,必须继承:from rest_framework.authentication import BaseAuthentication

      其他认证类:BasicAuthentication

    from rest_framework import exceptions
    from api import models
    from rest_framework.authentication import BaseAuthentication
    
    
    class FirstAuthtication(BaseAuthentication):
        def authenticate(self,request):
            pass
    
        def authenticate_header(self, request):
            pass
    
    class Authtication(BaseAuthentication):
        def authenticate(self,request):
            token = request._request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise exceptions.AuthenticationFailed('用户认证失败')
            # 在rest framework内部会将整个两个字段赋值给request,以供后续操作使用
            return (token_obj.user, token_obj)
    
        def authenticate_header(self, request):
            return 'Basic realm="api"'
    View Code

     使用:

      创建类:继承BaseAuthentication; 实现:authenticate方法

      返回值:None,我不管了,下一认证来执行;raise exceptions.AuthenticationFailed('用户认证失败') # from rest_framework import exceptions ; (元素1,元素2) # 元素1赋值给request.user; 元素2赋值给request.auth

      局部使用:在view试图类中直接调用

    from rest_framework.authentication import BaseAuthentication,BasicAuthentication
                        class UserInfoView(APIView):
                            """
                            订单相关业务
                            """
                            authentication_classes = [BasicAuthentication,]
                            def get(self,request,*args,**kwargs):
                                print(request.user)
                                return HttpResponse('用户信息')
    View Code

      全局使用:就是在settings文件中加上以下操作:

    REST_FRAMEWORK = {
                            # 全局使用的认证类
                            "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
                            # "UNAUTHENTICATED_USER":lambda :"匿名用户"
                            "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
                            "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
                        }
    View Code

     源码流程:

    - dispatch
    	- 封装request
    		- 获取定义的认证类(全局/局部),通过列表生成时创建对象。
    	- initial
    		- perform_authentication
    			request.user(内部循环....)
    

     六 权限

     不用视图,不用权限访问

     基本使用:

    class MyPermission(object):
    
            def has_permission(self,request,view):
                if request.user.user_type != 3:
                return False
            return True
                                                
                
    class OrderView(APIView):
        """
        订单相关业务(只有SVIP用户有权限)
                        """
        permission_classes = [MyPermission,]
                        
        def get(self,request,*args,**kwargs):
            # request.user
            # request.auth
            self.dispatch
            ret = {'code':1000,'msg':None,'data':None}
            try:
                ret['data'] = ORDER_DICT
            except Exception as e:
                pass
            return JsonResponse(ret)    
    View Code

     源码流程:

     另一种使用:类,必须继承:BasePermission,必须实现:has_permission方法

    from rest_framework.permissions import BasePermission
    
    class SVIPPermission(BasePermission):
        message = "必须是SVIP才能访问"
        def has_permission(self,request,view):
            if request.user.user_type != 3:
                return False
            return True
    - 返回值:    
        - True, 有权访问
        - False,无权访问
    - 局部 :在view视图文件
    class UserInfoView(APIView):
    """
    订单相关业务(普通用户、VIP)
    """
    permission_classes = [MyPermission1, ]
    
        def get(self,request,*args,**kwargs):
            return HttpResponse('用户信息')
    
    - 全局 :settings文件中
    REST_FRAMEWORK = {
        "DEFAULT_PERMISSION_CLASSES:['api.utils.permission.SVIPPermission']
            }
                    
    View Code

     源码流程:

    七 节流(控制访问频率)

     控制访问频率:

      创建访问次数: 类, 继承:BaseThrottle,实现:allow_request、wait
              类, 继承:SimpleRateThrottle,实现:get_cache_key、scope = "Luffy"(配置文件中的key)

    import time
    VISIT_RECORD = {}
    
    class VisitThrottle(object):
        """60s内只能访问3次"""
    
        def __init__(self):
            self.history = None
    
        def allow_request(self,request,view):
            # 1. 获取用户IP
            remote_addr = request.META.get('REMOTE_ADDR')
            ctime = time.time()
            if remote_addr not in VISIT_RECORD:
                VISIT_RECORD[remote_addr] = [ctime,]
                return True
            history = VISIT_RECORD.get(remote_addr)
            self.history = history
    
            while history and history[-1] < ctime - 60:
                history.pop()
    
            if len(history) < 3:
                history.insert(0,ctime)
                return True
    
            # return True    # 表示可以继续访问
            # return False # 表示访问频率太高,被限制
    
        def wait(self):
            """
            还需要等多少秒才能访问
            :return:
            """
            ctime = time.time()
            return 60 - (ctime - self.history[-1])
    View Code

      局部使用:在view视图中

    class AuthView(APIView):
        """
        用于用户登录认证
        """
        authentication_classes = []
        permission_classes = []
        throttle_classes = [VisitThrottle,]
    
        def post(self,request,*args,**kwargs):
    
            ret = {'code':1000,'msg':None}
            try:
                user = request._request.POST.get('username')
                pwd = request._request.POST.get('password')
                obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
                if not obj:
                    ret['code'] = 1001
                    ret['msg'] = "用户名或密码错误"
                # 为登录用户创建token
                token = md5(user)
                # 存在就更新,不存在就创建
                models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
                ret['token'] = token
            except Exception as e:
                ret['code'] = 1002
                ret['msg'] = '请求异常'
    
            return JsonResponse(ret)
    View Code

     源码流程:

     内置控制频率类:

    from rest_framework.throttling import BaseThrottle,SimpleRateThrottle
    class VisitThrottle(SimpleRateThrottle):
        scope = "Luffy"
    
        def get_cache_key(self, request, view):
            return self.get_ident(request)
    
    
    class UserThrottle(SimpleRateThrottle):
        scope = "LuffyUser"
    
        def get_cache_key(self, request, view):
            return request.user.username
            
    View Code

     全局使用:在settings文件中

    REST_FRAMEWORK = {
                    # 全局使用的认证类
                    "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
                    # "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ],
                    # "UNAUTHENTICATED_USER":lambda :"匿名用户"
                    "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
                    "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
                    "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'],
                    "DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
                    "DEFAULT_THROTTLE_RATES":{
                        "Luffy":'3/m',
                        "LuffyUser":'10/m',
                    }
                }
    View Code

     八 版本

     url通过get传参:

      自定义:

    http://127.0.0.1:8000/api/users/?version=v2
                    
    class ParamVersion(object):
            def determine_version(self, request, *args, **kwargs):
            version = request.query_params.get('version')
                return version
    
    class UsersView(APIView):
    
        versioning_class = ParamVersion
        def get(self,request,*args,**kwargs):
            #version = request._request.GET.get('version')
            #print(version)
            # version = request.query_params.get('version')
            # print(version)
    
            print(request.version)
            return HttpResponse('用户列表')    
    View Code

     在URL中传参(推荐使用):在urls文件中

    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view()),
    ]
    View Code

     在settings文件中:

    REST_FRAMEWORK = {
                    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    "DEFAULT_VERSION":'v1',
    "ALLOWED_VERSIONS":['v1','v2'],
    "VERSION_PARAM":'version',
                }
    View Code

     在views文件中:

    class UsersView(APIView):
    
        def get(self,request,*args,**kwargs):
            print(request.version)
            return HttpResponse('用户列表')
    View Code

     总结:

                            使用:
                    配置文件:
                        REST_FRAMEWORK = {
                            "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
                            "DEFAULT_VERSION":'v1',
                            "ALLOWED_VERSIONS":['v1','v2'],
                            "VERSION_PARAM":'version',
                        }
                    路由系统:
                                            
                        urlpatterns = [
                            # url(r'^admin/', admin.site.urls),
                            url(r'^api/', include('api.urls')),
                        ]
    
                        urlpatterns = [
                            # url(r'^admin/', admin.site.urls),
                            url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(),name='uuu'),
                        ]
                                
                    
                    视图:
                        
                        class UsersView(APIView):
    
                        def get(self,request,*args,**kwargs):
                    
                            # 1. 获取版本
                            print(request.version)
                            
                            # 2. 获取处理版本的对象
                            print(request.versioning_scheme)
    
                            # 3. 反向生成URL(rest framework)
                            u1 = request.versioning_scheme.reverse(viewname='uuu',request=request)
                            print(u1)
    
                            # 4. 反向生成URL
                            u2 = reverse(viewname='uuu',kwargs={'version':2})
                            print(u2)
    
                            return HttpResponse('用户列表')
                                    
    View Code

    九 解析器

     前戏:django:request.POST/ request.body

                           1. 请求头要求:
                    Content-Type: application/x-www-form-urlencoded
                    PS: 如果请求头中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值(去request.body中解析数据)。
                2. 数据格式要求:
                      name=alex&age=18&gender=男
            
                如:
                    a. form表单提交
                        <form method...>
                            input...
                            
                        </form>
                        
                    b. ajax提交
                        $.ajax({
                            url:...
                            type:POST,
                            data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
                        })
                        
                        情况一:
                            $.ajax({
                                url:...
                                type:POST,
                                headers:{'Content-Type':"application/json"}
                                data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
                            })
                            # body有值;POST无
                        情况二:
                            $.ajax({
                                url:...
                                type:POST,
                                headers:{'Content-Type':"application/json"}
                                data:JSON.stringfy({name:alex,age=18}) # {name:alex,age:18...}
                            })
                            # body有值;POST无
                            # json.loads(request.body)
                        
            rest_framework 解析器,对请求体数据进行解析
                
    View Code

     总结:

                              使用:    
                    配置:
                        REST_FRAMEWORK = {
                            "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
                            "DEFAULT_VERSION":'v1',
                            "ALLOWED_VERSIONS":['v1','v2'],
                            "VERSION_PARAM":'version',
                            
                            "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser']
                        }
                                    
                    使用:
                        class ParserView(APIView):
                            # parser_classes = [JSONParser,FormParser,]
                            """
                            JSONParser:表示只能解析content-type:application/json头
                            JSONParser:表示只能解析content-type:application/x-www-form-urlencoded头
                            """
    
                            def post(self,request,*args,**kwargs):
                                """
                                允许用户发送JSON格式数据
                                    a. content-type: application/json
                                    b. {'name':'alex',age:18}
                                :param request:
                                :param args:
                                :param kwargs:
                                :return:
                                """
                                """
                                1. 获取用户请求
                                2. 获取用户请求体
                                3. 根据用户请求头 和 parser_classes = [JSONParser,FormParser,] 中支持的请求头进行比较
                                4. JSONParser对象去请求体
                                5. request.data
                                """
                                print(request.data)
    
    
                                return HttpResponse('ParserView')
                                
            
                源码流程 & 本质:
                    a. 本质 
                        请求头 :...
                        状态码: ...
                        请求方法:...
                    b. 源码流程 
                        - dispatch: request封装 
                        - request.data 
                    
    View Code

    十 序列化

     部分总结:

      1. 写类

    class RolesSerializer(serializers.Serializer):
            id = serializers.IntegerField()
        title = serializers.CharField()
                
    class UserInfoSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            # fields = "__all__"
            fields = ['id','username','password',]
    View Code

      2. 字段

    a. title = serializers.CharField(source="xxx.xxx.xx.xx")
    b. title = serializers.SerializerMethodField()
    class UserInfoSerializer(serializers.ModelSerializer):
            rls = serializers.SerializerMethodField()  # 自定义显示
    
        class Meta:
            model = models.UserInfo
            fields = ['id','username','password','rls',]
                                
        # 自定义方法
        def get_rls(self, row):
                role_obj_list = row.roles.all()
    
            ret = []
            for item in role_obj_list:
                ret.append({'id':item.id,'title':item.title})
            return ret
    c. 自定义类
                            
    View Code

      3 自动序列化连表

    class UserInfoSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            # fields = "__all__"
            fields = ['id','username','password','group','roles']
            depth = 1 # 0 ~ 10
    View Code

      4. 生成链接

                                            class UserInfoSerializer(serializers.ModelSerializer):
                            group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='xxx')
                            class Meta:
                                model = models.UserInfo
                                # fields = "__all__"
                                fields = ['id','username','password','group','roles']
                                depth = 0 # 0 ~ 10
    
                        class UserInfoView(APIView):
                            def get(self,request,*args,**kwargs):
    
                                users = models.UserInfo.objects.all()
    
                                ser = UserInfoSerializer(instance=users,many=True,context={'request': request})
                                ret = json.dumps(ser.data, ensure_ascii=False)
                                return HttpResponse(ret)
                    
    View Code

     源码:

    对象, Serializer类处理;
    QuerySet,ListSerializer类处理;
    # ser.data
    View Code

    十一 数据请求校验

                                    class XXValidator(object):
                        def __init__(self, base):
                            self.base = base
    
                        def __call__(self, value):
                            if not value.startswith(self.base):
                                message = '标题必须以 %s 为开头。' % self.base
                                raise serializers.ValidationError(message)
    
                        def set_context(self, serializer_field):
                            """
                            This hook is called by the serializer instance,
                            prior to the validation call being made.
                            """
                            # 执行验证之前调用,serializer_fields是当前字段对象
                            pass
    
                    class UserGroupSerializer(serializers.Serializer):
                        title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[XXValidator('老男人'),])
    
    
                    class UserGroupView(APIView):
    
                        def post(self,request,*args,**kwargs):
    
                            ser = UserGroupSerializer(data=request.data)
                            if ser.is_valid():
                                print(ser.validated_data['title'])
                            else:
                                print(ser.errors)
    
                            return HttpResponse('提交数据')
                    
    View Code

     注意问题: 自定义验证规则时,需要钩子函数?请问钩子函数如何写?

     十二 分页

     分页在这里分为了三部分:普通分页,基于limit offset分页和基于加密的分页

      Response:直接就可以序列化,不需要json。

     普通分页:查看第几页,显示几条数据,但是如果数据量大的话,分页约往后面越慢

    from rest_framework.pagination import PageNumberPagination
    
    class MyPageNumberPagination(PageNumberPagination):
    
        page_size = 2
        page_size_query_param = 'size'
        max_page_size = 5
        page_query_param = 'page'
    
    class Pager1View(APIView):
    
        def get(self,request,*args,**kwargs):
    
            # 获取所有数据
            roles = models.Role.objects.all()
    
            # 创建分页对象
            # pg = MyPageNumberPagination()
            pg = PageNumberPagination()
    
            # 在数据库中获取分页的数据
            pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
    
            # 对数据进行序列化
            ser = PagerSerialiser(instance=pager_roles, many=True)
    
            return Response(ser.data)
            # return pg.get_paginated_response(ser.data)
    View Code

     基于limit offset的分页:在n个位置,向后面查看n条数据。LimitOffsetPagintion:基于limit offset分页的原生类

    from api.utils.serializsers.pager import PagerSerialiser
    from rest_framework.response import Response
    from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
    
    
    class MyLimitOffsetPagination(LimitOffsetPagination):
        default_limit = 2
        limit_query_param = 'limit'
        offset_query_param = 'offset'
        max_limit = 5
    
    
    class Pager1View(APIView):
    
        def get(self,request,*args,**kwargs):
    
                # 获取所有数据
            roles = models.Role.objects.all()
    
            # 创建分页对象
            # pg = MyLimitOffsetPagination()
            pg = LimitOffsetPagination()
    
            # 在数据库中获取分页的数据
            pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
    
            # 对数据进行序列化
            ser = PagerSerialiser(instance=pager_roles, many=True)
    
            return Response(ser.data)
            # return pg.get_paginated_response(ser.data)
    View Code

     基于加密的分页:记住每一页的最大值和最小值的ID,如果点击下一页就从最大值的ID向后数n条数据;如果点击的是上一页,就从最小值向前数n条数据。cursorParination:加密分页,不需要传入任何的参数。

    from api.utils.serializsers.pager import PagerSerialiser
    from rest_framework.response import Response
    from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
    
    class MyCursorPagination(CursorPagination):
        cursor_query_param = 'cursor'
        page_size = 2
        ordering = 'id'
        page_size_query_param = None
        max_page_size = None
    
    class Pager1View(APIView):
    
        def get(self,request,*args,**kwargs):
    
            # 获取所有数据
            roles = models.Role.objects.all()
    
            # 创建分页对象
            # pg = CursorPagination()
            pg = MyCursorPagination()
    
            # 在数据库中获取分页的数据
            pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
    
            # 对数据进行序列化
            ser = PagerSerialiser(instance=pager_roles, many=True)
    
            # return Response(ser.data)
            return pg.get_paginated_response(ser.data)
    View Code

     Pagination下面PageNumberpagination:分页器的一个模块,在settings文件中配置一下PAGE_SIZE:每页数据的条数。

    REST_FRAMEWORK = {
       
        "PAGE_SIZE":2,
    
    }
    View Code

    十二 视图

     过去:

    class Pager1View(View):
        pass
    View Code

      现在:

    class Pager1View(APIView): # View
            pass
    View Code

     无用:

    from api.utils.serializsers.pager import PagerSerialiser
    from rest_framework.generics import GenericAPIView
    
    class View1View(GenericAPIView): # APIView
        queryset = models.Role.objects.all()
        serializer_class = PagerSerialiser
        pagination_class = PageNumberPagination
        def get(self,request,*args,**kwargs):
            # 获取数据
            roles = self.get_queryset() # models.Role.objects.all()
    
            # [1, 1000,]     [1,10]
            pager_roles = self.paginate_queryset(roles)
    
            # 序列化
            ser = self.get_serializer(instance=pager_roles,many=True)
    
            return Response(ser.data)
        
    View Code

     GenericViewSet(ViewSetMixin, generics.GenericAPIView):

      路由:

    url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list'})),
    View Code

      视图:

    from api.utils.serializsers.pager import PagerSerialiser
    from rest_framework.viewsets import GenericViewSet
    
    class View1View(GenericViewSet):
        queryset = models.Role.objects.all()
        serializer_class = PagerSerialiser
        pagination_class = PageNumberPagination
    
        def list(self, request, *args, **kwargs):
            # 获取数据
            roles = self.get_queryset()  # models.Role.objects.all()
    
            # [1, 1000,]     [1,10]
            pager_roles = self.paginate_queryset(roles)
    
            # 序列化
            ser = self.get_serializer(instance=pager_roles, many=True)
    
            return Response(ser.data)
                            
    View Code

     增删改查:

      路由:as_view():括号里面是可以传入参数的。

    url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})),
    url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>d+)/$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
    View Code

      视图:

    from api.utils.serializsers.pager import PagerSerialiser
    from rest_framework.viewsets import GenericViewSet,ModelViewSet
    from rest_framework.mixins import ListModelMixin,CreateModelMixin
    
    class View1View(ModelViewSet):
        queryset = models.Role.objects.all()
        serializer_class = PagerSerialiser
        pagination_class = PageNumberPagination
                        
    View Code

     总结:

    a. 增删改查 ModelViewSet
        b. 增删     CreateModelMixin,DestroyModelMixin  GenericViewSet
        c. 复杂逻辑  GenericViewSet 或 APIView 
                
        PS: 还债: 
            GenericAPIView.get_object
                check_object_permissions
                    has_object_permission
    View Code

    十三 路由

     在url后面加上format=json:写给解析器看的,也等于早url里面加上一个命名分组,format一样的

     自动生成url:urls文件导入routers模块;然后再router=routers.DefaultRouter();最后在router.register(r'...',views.类名);最最后url(r'^',incloude(router.urls))。以上四步是是自动生成增删改查的url的步骤。

    a. 
        url(r'^(?P<version>[v1|v2]+)/parser/$', views.ParserView.as_view()),
                
    b. 
        url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})),
            
    c. 
        # http://127.0.0.1:8000/api/v1/v1/?format=json
        url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})),
        # http://127.0.0.1:8000/api/v1/v1.json
        url(r'^(?P<version>[v1|v2]+)/v1.(?P<format>w+)$', views.View1View.as_view({'get': 'list','post':'create'})),
    
        url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>d+)/$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
        url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>d+).(?P<format>w+)$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
    d. 
    from api import views
    from rest_framework import routers
    
    
    router = routers.DefaultRouter()
    router.register(r'xxxxx', views.View1View)
    router.register(r'rt', views.View1View)
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
    ]
    View Code

    十四 渲染器

     JSONRenderer:json渲染

      在views文件中:局部渲染renderer_classes = [JSONRenderer,BrowsableAPIRenderer]

    from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
    
    class TestView(APIView):
            renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
        def get(self, request, *args, **kwargs):
            # 获取所有数据
            roles = models.Role.objects.all()
    
            # 创建分页对象
            # pg = CursorPagination()
            pg = MyCursorPagination()
    
            # 在数据库中获取分页的数据
            pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
    
            # 对数据进行序列化
            ser = PagerSerialiser(instance=pager_roles, many=True)
    
            return Response(ser.data)
    View Code

      在settings文件中全局渲染:

       views文件:

    from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
    
    class TestView(APIView):
            
        def get(self, request, *args, **kwargs):
            # 获取所有数据
            roles = models.Role.objects.all()
    
            # 创建分页对象
            # pg = CursorPagination()
            pg = MyCursorPagination()
    
            # 在数据库中获取分页的数据
            pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
    
            # 对数据进行序列化
            ser = PagerSerialiser(instance=pager_roles, many=True)
    
            return Response(ser.data)
                    
    View Code

       settings文件中

    REST_FRAMEWORK = {
            
                
        "DEFAULT_RENDERER_CLASSES":[
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',
        ]
    }
                    
    View Code

     找模板的顺序:先去最外层查找,如果没有再去某个app里面寻找

     十五 面试题

     我的写的类的试图最多继承了几个类

      6个类中有四个是增删改查的类,有一个是局部更新的类,还有一个类是专门继承其他的类的。

      

     你做过分页吗,数据量有多大:django自带的分页功能不完善,我自己定义过分页,数据量差不多2000左右。数据量越大,分页向后查找的速度就会越慢。在数据量非常大的情况下,数据没有必要全部的显示,显示一定的数量就可以,避免数据量多的分页。

     rest_framowork的请求生命周期:

    1.请求到达服务端,经过WSGI和中间件到达路由系统
    2.路由系统执行配置的CBV或者FBV中的dispatch方法
    3.在dispatch方法中,request方法被封装添加了解析器,认证方法及选择器等方法
    4.然后执行initial方法
    5.再获取版本,进行认证操作,权限操作和节流操作
    6.最后执行自定义的get,post,push,delete等自定义方法
    7.在执行initial方法之前,通过try来捕获可能出现的异常
    8.如果出现异常,就执行handle_exception方法来处理捕获到的异常
    9.不管是否出现异常,最后的返回值都通过finalize_response方法来处理响应的内容
    View Code

     什么请况下request.POST没有值:

    只有请求header中的'Content-Type'是'application/x-www-form-urlencoded'request.POST'的情况下POST里面才会有值,,其它情况下就会没有值
    

      序列化检验分为哪些步骤:

    自己写的类继承:serializer
    
    字段继承:SerializerMethodField。
    
    生成url
    View Code

     

  • 相关阅读:
    余佳文 超级课程表创始人
    JavaScript 运行机制详解:再谈Event Loop
    koa2 async和await 实战详解
    node.js
    Mac版MySQL安装最新教程
    nw.js 打包与发布
    【514】keras Dense 层操作三维数据
    【513】keras 后端函数说明
    【512】keras 中的核心网络层
    【511】Keras 函数式 API
  • 原文地址:https://www.cnblogs.com/fangjie0410/p/8511938.html
Copyright © 2011-2022 走看看