• Django之REST framework源码分析


    前言:

    Django REST framework,是1个基于Django搭建 REST风格API的框架;

    1、什么是API呢?

    API就是访问即可获取数据的url地址,下面是一个最简单的 Django API,访问http://127.0.0.1:8000/,返回用户列表;

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^user/', views.user),
    ]
    路由
    from django.shortcuts import render,HttpResponse
    from django.http import JsonResponse
    users=['大波','张开','人间' ]
    def user(request):
        return JsonResponse(users,safe=False)
    视图

    2、什么是restfunAPI呢?

    如果新增增加用户功能,再向用户暴露1个API接口  http://127.0.0.1:8000/useradd/?new_user='A'

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^user/', views.user),
        url(r'^useradd/', views.user_add), #增加用户功能
    ]
    路由
    from django.shortcuts import render,HttpResponse
    from django.http import JsonResponse
    users=['大波','张开','人间' ]
    
    
    def user(request):
        return JsonResponse(users,safe=False)
    
    def user_add(request): #增加用户功能
        new_user=request.GET.get('new_user')
        users.append(new_user)
        return JsonResponse(users,safe=False)
    视图

    在增加用户功能上再添加删除、编辑用户功能 向用户暴露 4个api接口

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^user/', views.user),                     #显示用户列表
        url(r'^useradd/$', views.user_add),             #增加用户功能
        url(r'^userdelete/d+/$', views.user_delete),   # 删除用户功能
        url(r'^useredit/d+/$', views.user_edit),       #编辑用户信息
    ]
    路由

    如果按照这种开发模式,随着项目功能的完善,弊端也会暴露出来,维护的API接口也会越来越多,在协作开发中出现种种问题;

    于是一种API编写规范出现了,他就是RESTful规范;

    在1个视图中通过判断用户的请求method不同(get/post/put/delete),后台做不同的操作;这样就可以只暴露1个API给用户并且维护了代码的整洁,这就是restful 规范之一;

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^user/', views.user),                     #显示用户列表
        url(r'^user/(?P<pk>d+)/$',views.user),         #编辑和删除需要一个PK 在URL中
    
    ]
    路由
    from django.shortcuts import render,HttpResponse
    from django.http import JsonResponse
    users=['大波','张开','人间' ]
    
    
    def user(request,*args,**kwargs):
        if request.method=='GET':
            #如果请求方法为 GET  获取数据列表
            return JsonResponse(users,safe=False)
    
        elif request.method=='POST':
            # 如果请求方法为 POST  添加列表数据
            return JsonResponse(users, safe=False)
    
        elif request.method=='PUT':
            # 如果请求方法为 PUT,更新操作
            pk=kwargs.get('pk')
            return JsonResponse(users, safe=False)
    
        elif request.method=='DELETE':
            pk=kwargs.get('pk')
            # 如果请求方法为 DELETE,删除操作
            return JsonResponse(users, safe=False)
    视图

    总结:

    restful 风格API有很多规范,接下来我们介绍一下它都有哪些规范?

    3、REST framework 请求的生命周期

    一、RESTful API设计规范 

    RESTful API的设计需要遵循很多规范,但是只是建议,如果不遵守这种规范也是可以的,以下是 它部分规范介绍;

    1、面向资源

    面向资源指得是把网络中可以访问到的任何url都想象成1个资源,这个资源可以是1个文件、 1张图片、1个视频....。

    例如:

    http://www.le.com/videos/  

    http://www.le.com/files/

    http://www.le.com/pictures/

     注意 1级域名后边(videos、files、pictures)都是名词

    2、版本号

    我们的API不是一成不变的,如果版本迭代也需要体现在url上,以供用户选择不同版本;

    例如:

    http://www.le.com/v1/files/  访问版本1API资源

    http://www.le.com/v2/files/  访问版本2API资源

     3、返回值

    API在放回数据的同时也返回请求的状态码;

    例如:

    return HttpRespose(josn.dumps(ret),status=200) 访问成功

    return HttpRespose(josn.dumps(ret),status=3XX) 权限错误

    return HttpRespose(josn.dumps(ret),status=4XX) 资源存在

    return HttpRespose(josn.dumps(ret),status=5XX) 服务器错误

    详细:

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

    4、API与用户的通信协议,总是使用https协议

    5、域名规范

    例如:

    前端Vue使用:https://www.le.com

    后端Django:https://api.le.com   注意会出现跨域问题

    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、返回结果: 根据用户不同的操作,服务器放过的结果应该符合以下规范

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

     

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

    [
                        {
                            'name':'alex',
                            'age':'18',
                            'user_type': 'http://www.le.com/api/v1/usergroups/1/'
                        },
                        {
                            'name':'alex',
                            'age':'18',
                            'user_type': 'DS'
                        },
                        {
                            'name':'alex1',
                            'age':'18',
                            'user_type': 'DS'
                        },
                        {
                            'name':'alex2',
                            'age':'18',
                            'user_type': 'DS'
                        }
    
                    ]
    View Code

    9.总结

    restful 规范本质就是1个约束 web 接口,增、删、改、查方法的规范,方便调用方维护、记忆url;

    三. 基于DjangoRest Framework框架实现 restful API

     如果自己去实现1个restful api需要遵循以上很多restful API规则,实现起来比较困难,

    于是Django的 Rest framework框架出现了,通过它就可以无需关注众多restful规范,  快速实现restful API;

    0、安装 djangorestframwork
    pip install djangorestframework -i https://pypi.douban.com/simple/
    
    
    1、安装 virtual env(虚拟环境,相当于做了环境隔离)
    pip install virtualenv
    
    
    2、创建1个存放虚拟环境的目录
    mkdir virtualenv
    cd virtualenv
    
    3、创建1个干净的虚拟环境(--no-site-packaes)
    
    virtualenv --no-site-package
    Using base prefix 'd:\python3.6.1'
    New python executable in D:virtualenvven
    Installing setuptools, pip, wheel...done.
    
    4、激活虚拟环境
    linux
    source venv/bin/activate
    
    DOS
    cdvirtualenvvenv1Scripts
    activate
    
    安装Django
    >>> exit()
    (venv1) D:virtualenvvenv1Scripts>pip install django
    
    5、退出虚拟环境
    
    deactivate
    前戏 Virtual env

    Django framework源码分析

    想要深入了解 Rest framework框架是如何实现restful API的就要分析它的源码;

    Django framework源码入口

    由于所有CBV首先要执行dispatch方法,所有它就是Django framework源码的入口

    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView   #导入 APIView
    
    
    #类继承了APIView就是 restfulAPI了,但是APIView继承了Django的view本质是对Django view的一种二次封装;
    class UsersView(APIView):
        def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
    #1.二次封装request(原request,parsers=解析用户请求的数据,authenticators=认证相关, negotiator=选择相关, parser_context='封装的self和参数')
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
    #2、request被更换为封装完毕的request
            self.headers = self.default_response_headers  # deprecate?
    #3、执行initial 获取版本信息、 认证、权限、节流
            try:
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                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)
    
            except Exception as exc:
                response = self.handle_exception(exc)
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
        def get(self,request,*args,**kwargs):
            self.dispatch()   # 执行APIView的 dispatch 也是 framwork的源码入口
            return HttpResponse('...')
        #
    View Code

    Django framework分析结果

    经过对Django framework分析得知 Django restframework的生命周期

    1、中间件
    2、路由系统
    3、CBV/FBV(视图)
    4、执行dispath方法
    二次封装request(原request,解析用户请求的数据,authenticators=认证相关, 
    
    negotiator=选择相关, parser_context='封装的self和参数')
    
    
    try  
       获取版本信息
       认证
       权限
       节流
       respose=执行 GET/POST/PUT/DELETE方法
    
    except
       respose = 异常
    
    finally
       处理响应内容
    View Code

    1、版本号设置

    我们的API不是一成不变的,如果版本迭代就需要以版本号加以区别,让用户选择不同的版本;

    1.1 版本控制源码分析

    View Code

    1.2  配置使用

    视图配置

    方式1: http://127.0.0.1:8000/users/?version=v1

    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView   #导入 APIView
    from rest_framework.versioning import QueryParameterVersioning
    from rest_framework.versioning import URLPathVersioning
    
    class UsersView(APIView):
        versioning_class=QueryParameterVersioning  #http://127.0.0.1:8000/users/?version=v1
        def get(self,request,*args,**kwargs):
            print(request.version)
            return HttpResponse('...')
    View Code

    方式2:http://127.0.0.1:8000/v3/users/

    from rest_framework.views import APIView   #导入 APIView
    from rest_framework.versioning import QueryParameterVersioning
    from rest_framework.versioning import URLPathVersioning
    
    class UsersView(APIView):
        versioning_class = URLPathVersioning         #http://127.0.0.1:8000/v3/users/
        def get(self,request,*args,**kwargs):
            print(request.version)
            return HttpResponse('...')
    View Code

    全局配置

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01.apps.App01Config',
        'rest_framework'                       #组册 rest_framework APP
    ]
    
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
        "VERSION_PARAM":"version",                                                  #设置默认参数名称
        "DEFAULT_VERSION":'v1',                                                    #设置默认版本
        "ALLOWED_VERSIONS":['v1','v2']                                           #设置允许的版本
    }
    settings配置文件

    获取和反向生成url

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^(?P<version>w+)/users/$', views.UsersView.as_view(),name='xxxxx'),
        # url(r'^(?P<version>w+)/users/$', views.UsersView.as_view()),
    
        ]
    路由
    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView   #导入 APIView
    # from rest_framework.versioning import QueryParameterVersioning
    # from rest_framework.versioning import URLPathVersioning
    
    class UsersView(APIView):
        # versioning_class=QueryParameterVersioning  #http://127.0.0.1:8000/users/?version=v1
        # versioning_class = URLPathVersioning         #http://127.0.0.1:8000/v3/users/
        def get(self,request,*args,**kwargs):
            print(request.versioning_scheme.reverse(viewname='xxxxx',request=request))  #反向生成URL
            return HttpResponse('...')
    视图

    2、用户身份认证

    根据用户请求(tocken)做用户身份认证,成功request.user能获取到用户名,否则AnonymousUser /none;

    2.1  认证源码分析

    APIView 设置认证相关功能

    Request类调用认证功能

    class UsersView(APIView):
        def get(self,request,*args,**kwargs):
            self.dispatch()
            print(self.authentication_classes )
            return HttpResponse('...')
    视图
    class APIView(View):
          '''1.3 去系统配置文件,获取认证相关的类s [BasicAuthentication(),SessionAuthentication()]
          '''
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
                    
                
        def dispatch(self, request, *args, **kwargs):
            self.args = args
            self.kwargs = kwargs
            #1  二次封装 request(self.initialize_request)
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            #替换成新的request
            self.headers = self.default_response_headers  # deprecate?
            try:
                #2、执行认证功能
                self.initial(request, *args, **kwargs)
            except Exception as exc:
                pass
            return self.response    
                  
            
        def initialize_request(self, request, *args, **kwargs):
        
        '''1.1 执行initialize_reques,执行authenticators=self.get_authenticators() 
           调用get_authenticators()获取[auth对象,auth对象]
        '''
            return Request(
                request,
                authenticators=self.get_authenticators(),
             
            )    
    
        def get_authenticators(self):
        '''1.2 get_authenticators 去系统配置文件中获取 认证相关的类,
            返回列表'''
            return [auth() for auth in self.authentication_classes]    
            
        #2.1 认证功能调用perform_authentication方法
        def initial(self, request, *args, **kwargs):
            self.perform_authentication(request)
        #2.2 perform_authentication调用了 request类的user方法
         def perform_authentication(self, request):
            request.user
    1、把 [认证对象,认证对象]封装到Request
    #2、调用request的user方法,执行认证功能
    class Request():
        def __init__(request,authenticators=None):
            self._request = request    
            self.authenticators = authenticators or () #or 元祖
            
        #2.1  request.user方法调用了 _authenticat方法
        @property
        def user(self):
            if not hasattr(self, '_user'):
                self._authenticate()
            return self._user    
                        
        def _authenticate(self):
           
            # 2.2遍历  self.authenticators,request初始环节封装的[认证对象,认证对象 ],并执行认证对象的authenticate方法
            for authenticator in self.authenticators:
                '''
                异常终止整个循环
                返回元祖,整个循环终止
                返回None,进入下一次循环 
                '''
                try:
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
    
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    self.user, self.auth = user_auth_tuple
                    return
            self._not_authenticated()     
            
        #2.3 如果循环出现 异常,则设置 request.user = 匿名用户    
        def _not_authenticated(self):
            self._authenticator = None
    
            if api_settings.UNAUTHENTICATED_USER:
                self.user = api_settings.UNAUTHENTICATED_USER()
            else:
                self.user = None
    
            if api_settings.UNAUTHENTICATED_TOKEN:
                self.auth = api_settings.UNAUTHENTICATED_TOKEN()
            else:
                self.auth = None
    2、调用request的user方法,执行认证功能

     2.2 使用配置

    通过对 APIView 认证环节的源码分析,得知认证环节本质就是执行 BasicAuthentication、SessionAuthentication类中的authenticate方法;

    如果authenticate方法执行成功 return (username,None ) ,则设置 request.user属性为username,否则设置为AnonymousUser匿名用户;

    如果authenticate方法执行成功 return  None  ,则继续执行 下一个认证类的authenticate方法;

    如果authenticate方法 raise exceptions.AuthenticationFailed('认证失败')抛出异常,则整个认证环节终止;

    局部视图使用认证功能 

    # 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK  里的东西
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
        "VERSION_PARAM":"version",                                                  #设置默认参数名称
        "DEFAULT_VERSION":'v1',                                                    #设置默认版本
        "ALLOWED_VERSIONS":['v1','v2'] ,                                          #设置允许的版本
        # 设置为 None,没有通过认证的用户,request.user匿名用户 =None
        "UNAUTHENTICATED_USER":None,
        # 设置为UNAUTHENTICATED_TOKEN   tocken =None
        'UNAUTHENTICATED_TOKEN':None
    }
    配置文件
    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView   #导入 APIView
    from rest_framework.authentication import BasicAuthentication
    from rest_framework.authentication import SessionAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    class CustomAuthentication(BasicAuthentication):
        def authenticate(self, request):
            return ('egon',None )     #authenticate 返回哪个用户,request.user=哪个用户
        def authenticate_header(self, request):
            pass
    
    class UsersView(APIView):
        authentication_classes =[CustomAuthentication ]
        def get(self,request,*args,**kwargs):
            print(request.user)
            return HttpResponse('...')
    视图

    自定制认证失败错误信息

    from rest_framework.authentication import SessionAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    class CustomAuthentication(BasicAuthentication):
        def authenticate(self, request):
            #自定义认证失败异常信息
            raise exceptions.AuthenticationFailed('认证失败')
        def authenticate_header(self, request):
            pass
    
    class UsersView(APIView):
        authentication_classes =[CustomAuthentication ]
        def get(self,request,*args,**kwargs):
            print(request.user)
            return HttpResponse('...')
    View Code

    全局视图使用认证功能

    from rest_framework.authentication import BasicAuthentication
    from rest_framework.authentication import SessionAuthentication
    class CustomAuthentication(BasicAuthentication):
        def authenticate(self, request):
            return ('alex',None)
        def authenticate_header(self, request):
            pass
    utils.py
    # 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK  里的东西
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
        "VERSION_PARAM":"version",                                                  #设置默认参数名称
        "DEFAULT_VERSION":'v1',                                                    #设置默认版本
        "ALLOWED_VERSIONS":['v1','v2'] ,                                          #设置允许的版本
        # 设置为 None,没有通过认证的用户,request.user匿名用户 =None
        "UNAUTHENTICATED_USER":None,
        # 设置为UNAUTHENTICATED_TOKEN   tocken =None
        'UNAUTHENTICATED_TOKEN':None,
         "DEFAULT_AUTHENTICATION_CLASSES":(
            "app01.utils.CustomAuthentication",
    
         ),
    }
    settings.py配置文件
    from rest_framework.authentication import SessionAuthentication
    from rest_framework import exceptions
    
    
    class UsersView(APIView):
        def get(self,request,*args,**kwargs):
            print(request.user)
            # print(self.dispatch())
            return HttpResponse('...')
    views.py 视图

     2.3 扩展

    基于token的用户认证

    token:服务端动态生成的1串用来检验用户身份的字符串,可以放在header、cokies、url参数(安全性较差)、请求体(CSRF token);

    token和session类似,不同于 session的是token比较灵活,不仅仅可以cokies里;

    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView   #导入 APIView
    from rest_framework.authentication import BasicAuthentication
    from rest_framework.authentication import SessionAuthentication
    from rest_framework import exceptions
    from rest_framework.request import Request
    from rest_framework.response import Response
    from app01 import models
    from django.http import JsonResponse
    
    def gen_tcoken(username):
        import random,hashlib,time
        ctime=str(time.time())
        hash=hashlib.md5(username.encode('utf-8'))
        hash.update(ctime.encode('utf-8'))
        return hash.hexdigest()
    
    class CustomAuthentication(BasicAuthentication): #认证类
        def authenticate(self, request):
            #从 url的参数中获取客户端携带的tocken
            tocken=request.query_params.get('tocken')
            #去数据库中检查tocken是否存在?
            tocken_obj=models.Token.objects.filter(token=tocken).first()
            #如果存在
            if tocken_obj:
                #注意还可以返回对象
                return (tocken_obj.user,tocken_obj)
            #否则 自定义错误信息 认证失败
            raise exceptions.AuthenticationFailed('认证失败')
    
        def authenticate_header(self, request):
            pass
    
    
    class ZhanggenAuthentication():
        '''
        局部使用认证功能的情况下,优先继承该类
        '''
        authentication_classes = [CustomAuthentication]
    
    
    class LoginView(APIView): #允许匿名用户访问
        def post(self, request, *args, **kwargs):
            ret = {'coede': 1000, 'msg': ''}
            username = request.data.get('username')
            pwd = request.data.get('password')
            user = models.Userinfo.objects.filter(user=username, pwd=pwd)
            if user:
                # 根据user=user去查找,如果找到更新 如果没有找到创建defaults={} 中的数据
                tk = gen_tcoken(username)
                models.Token.objects.update_or_create(user=user, defaults={'token': tk})
                ret['coede'] = 1001
                ret['token'] = tk
            else:
                ret['msg'] = '用户名或者密码错误'
            return JsonResponse(ret)
    
    class IndexView(ZhanggenAuthentication,APIView): #不允许匿名用户访问
        def get(self,request,*args,**kwargs):
            print(request.user)
            print(request.user.user)
            return Response('通过认证!')
    View Code
    from django.db import models
    
    class Userinfo(models.Model):
        user=models.CharField(max_length=32)
        pwd=models.CharField(max_length=64)
        email=models.CharField(max_length=64)
        user_type_choices=(
            (1,'普通用户'),
            (2,'文艺用户'),
            (3,'其他用户'),
        )
        user_type_id=models.IntegerField(choices=user_type_choices,default=1)
    
    #设置 one to one 1个用户不能在不同设备上登录
    #设置 Freikey 支持1个用户 在不同设备上同时登录
    class Token(models.Model):
        user=models.OneToOneField(Userinfo)
        token=models.CharField(max_length=64)
    models.py
    def gen_tcoken(username):
        import random,hashlib,time
        ctime=str(time.time())
        hash=hashlib.md5(username.encode('utf-8'))
        hash.update(ctime.encode('utf-8'))
        return hash.hexdigest()
    动态Tocken生成器
    class LoginView(APIView): #允许匿名用户访问
        def post(self, request, *args, **kwargs):
            ret = {'coede': 1000, 'msg': ''}
            username = request.data.get('username')
            pwd = request.data.get('password')
            user = models.Userinfo.objects.filter(user=username, pwd=pwd)
            if user:
                # 根据user=user去查找,如果找到更新 如果没有找到创建defaults={} 中的数据
                tk = gen_tcoken(username)
                models.Token.objects.update_or_create(user=user, defaults={'token': tk})
                ret['coede'] = 1001
                ret['token'] = tk
            else:
                ret['msg'] = '用户名或者密码错误'
            return JsonResponse(ret)
    授权API接口生成Tocken并把Tocke返回给客户端。
    class CustomAuthentication(BasicAuthentication): #认证类
        def authenticate(self, request):
            #从 url的参数中获取客户端携带的tocken
            tocken=request.query_params.get('tocken')
            #去数据库中检查tocken是否存在?
            tocken_obj=models.Token.objects.filter(token=tocken).first()
            #如果存在
            if tocken_obj:
                #注意还可以返回对象
                return (tocken_obj.user,tocken_obj)
            #否则 自定义错误信息 认证失败
            raise exceptions.AuthenticationFailed('认证失败')
    
        def authenticate_header(self, request):
            pass
    RestApi认证类,检查客户端是否携带tocken?
    class ZhanggenAuthentication():
        '''
        局部使用认证功能的情况下,优先继承该类
        '''
        authentication_classes = [CustomAuthentication]
    
    class IndexView(ZhanggenAuthentication,APIView): #不允许匿名用户访问
        def get(self,request,*args,**kwargs):
            print(request.user)
            print(request.user.user)
            return Response('通过认证!')
    通过认证视图 request.user获取认证用户信息

    认证失败后定制响应头

        def authenticate_header(self, request):
            return 'Basic realm=api'
            #设置响应头,认证失败后,浏览器弹窗,再向server发送请求
    authenticate_header

    使用RestAPI认证功能小结

    1、创建2张表userinfo 和token表

    2、认证类的authenticate方法去请求头中获取token信息,然后去token表中查询token是否存在;

    3、查询到token 是正常用户(返回 用户名)否则为匿名用户(raise异常终止认证、或者 return none进行下一个认证)

    4、局部应用

    方式1::哪个CBV需要认证在类中定义authentication_classes =[CustomAuthentication ] 

    方式2:额外定义1个类,CBV多继承

    方式3:全局配置使用认证功能,那个CBV不使用authentication_classes =[ ]  

    5、全局使用 在配置文件中配置 ,注意重新创建一个模块,把认证类放里面;

    3、权限相关

    社会3、6、9,如何实现对API接口的访问权限设置呢?基于IP ?和用户名?

    需求:普通用户对UserGroupView视图无访问权限

    局部使用:

    from django.db import models
    
    class Userinfo(models.Model):
        user=models.CharField(max_length=32)
        pwd=models.CharField(max_length=64)
        email=models.CharField(max_length=64)
        user_type_choices=(
            (1,'普通用户'),
            (2,'文艺用户'),
            (3,'其他用户'),
        )
        user_type_id=models.IntegerField(choices=user_type_choices,default=1)
    
    #设置 one to one 1个用户不能在不同设备上登录
    #设置 Freikey 支持1个用户 在不同设备上同时登录
    class Token(models.Model):
        user=models.OneToOneField(Userinfo)
        token=models.CharField(max_length=64)
    models
    #权限相关
    from rest_framework.permissions import BasePermission,AllowAny
    
    class CustomPermission(BasePermission):
        def has_permission(self, request, view):
            #view代指那个视图
            #如果用户为普通用户,访问的视图是UserGroupView,没有权限拒绝访问
            if request.user.user_type_id==1 and isinstance(view,UserGroupView) :
    
                return False #返回False 代表没有权限访问
            return True       # return True  #返回True 代表都有权限访问
    自定义权限类
    class UserGroupView(APIView):
        permission_classes=[CustomPermission]
        def get(self, request, *args, **kwargs):
            # print(request.user)
            print(self.permission_classes)
            print(request.user.user)
            return Response('通过认证!')
    视图应用

    全局使用:

    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
        "VERSION_PARAM":"version",                                                  #设置默认参数名称
        "DEFAULT_VERSION":'v1',                                                    #设置默认版本
        "ALLOWED_VERSIONS":['v1','v2'] ,                                          #设置允许的版本
        # 设置为 None,没有通过认证的用户,request.user匿名用户 =None
        "UNAUTHENTICATED_USER":None,
        # 设置为UNAUTHENTICATED_TOKEN   tocken =None
        'UNAUTHENTICATED_TOKEN':None,
         "DEFAULT_AUTHENTICATION_CLASSES":(
            "utils.auth.auth1.CustomAuthentication",
            ),
        'DEFAULT_PERMISSION_CLASSES':[ "utils.permission.per1.CustomPermission",],
    }
    setings配置文件
    #权限相关
    from rest_framework.permissions import BasePermission,AllowAny
    from app01 import views
    class CustomPermission(BasePermission):
        message='无权限访问' #定制错误信息
        def has_permission(self, request, view):
            #view代指那个视图
            #如果用户为普通用户,访问的视图是UserGroupView,没有权限拒绝访问
            if request.user.user_type_id==1 and isinstance(view,views.UserGroupView) :
    
                return False #返回False 代表没有权限访问
            return True       # return True  #返回True 代表都有权限访问
    自定制权限类
    class UserGroupView(APIView):
        def get(self, request, *args, **kwargs):
            # print(request.user)
            print(self.permission_classes)
            print(request.user.user)
            return Response('通过认证!')
    视图

    RestApi访问权限使用小结:

    创建权限的类,类中has_permission(self, request, view) return flase代表没有权限访问,return True代表有权限访问,request代指请求信息、view参数代指视图  

    RestApi的访问权限控制工作在视图层 dispatch方法,RBAC工作在中间件; 

     

    4、限制用户访问频率

    Django Rest Framework 对用户的访问频率是通过在Django缓存中记录每位用户访问时间,进行访问频率限制的;

    用户访问时间记录:以用户唯一标识(IP/token)做键,访问时间戳列表做值,列表的长度为用户访问次数;

    {

    用户A:[ 访问时间戳1,访问时间戳2],

    用户B:[ 访问时间戳1,访问时间戳2]

    }

    当用户A请求到来:

    根据用户的唯一标识(IP/Token/用户名)获取用户A的所有访问记录时间戳列表;

    获取当前时间,根据当前时间戳和访问记录列表中的时间戳、列表中时间戳个数,做对比来决定用户A是否可以继续访问;

      #根据用户的唯一标识 获取当我用户的所有访问记录
            self.history = self.cache.get(self.key, [])
            #获取当前时间 [最近访问,最早访问  ]
            self.now = self.timer()
    
            # Drop any requests from the history which have now passed the
            # throttle duration
            #做限制请求频率操作
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()#控制用户访问记录列表的中的访问记录,最后1个在配置时间范围内
    
           #超出合理长度 返回False,不能继续访问
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            #访问记录列表在合理长度,return Ture可继续访问
            return self.throttle_success()
    核心源码

    4.1、局部应用(基于用户对单个视图进行访问频率限制self.key = request.user.user+view.__class__.__name__

    {

    用户A视图1:[ 访问时间戳1,访问时间戳2],

    用户A视图2:[ 访问时间戳1,访问时间戳2]

    }

    # 限制访问次数相关
    from rest_framework.throttling import BaseThrottle,AnonRateThrottle,SimpleRateThrottle
    #根据IP
    class CustomAnonThrottle(SimpleRateThrottle):
        # 代指配置文件中定义的访问频率的类 {'anon':'5/m'}
        scope = 'anon'
        def allow_request(self, request, view):
            # 已经登录管不着
            if request.user:
                return True
            # 获取当前访问用户的唯一标识 get_cache_key是抽象方法 ip+字符串格式化
            self.key = self.get_cache_key(request, view)
            # 根据用户的唯一标识 获取当我用户的所有访问记录
            self.history = self.cache.get(self.key, [])
            # 获取当前时间 [最近访问,最早访问  ]
            self.now = self.timer()
            # 做限制请求频率操作
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()  # 根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内
    
                # 根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
                # 超出合理长度 返回False,不能继续访问
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            # 访问记录列表在合理长度,return Ture可继续访问
            return self.throttle_success()
    
        def get_cache_key(self, request, view):
            return self.cache_format % {
                'scope': self.scope,
                'ident': self.get_ident(request)
            }
    
    #根据用户名
    class CustomUserThrottle(SimpleRateThrottle):
        #代指配置文件中定义的访问频率的类 {'anon':'5/m'}
        scope = 'user'
        def allow_request(self, request, view):
            #已经登录管不着
            if not request.user:
                return True
            #获取当前访问用户的唯一标识 用户名
            #self.key = request.user.user#用户对所页面进行访问频率限制
            #用户对单个视图 进行访问频率限制
            self.key = request.user.user+view.__class__.__name__
            #根据用户的唯一标识 获取当我用户的所有访问记录
            self.history = self.cache.get(self.key, [])
            #获取当前时间 [最近访问,最早访问  ]
            self.now = self.timer()
            #做限制请求频率操作
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()#根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内
    
           #根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
                # 超出合理长度 返回False,不能继续访问
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            #访问记录列表在合理长度,return Ture可继续访问
            return self.throttle_success()
    View Code

    4.1、全局应用(基于用户对每个视图进行访问频率限制self.key = request.user.user

    {

    用户A:[ 访问时间戳1,访问时间戳2],

    }

        url(r'^(?P<version>w+)/usergroup/$', views.UserGroupView.as_view(), name='xxxxx'), #登录用户 4/m
        url(r'^(?P<version>w+)/test/$', views.TestView.as_view(), name='xxxxx'), #匿名用户5/m
    路由系统
    # 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK  里的东西
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
        "VERSION_PARAM":"version",                                                  #设置默认参数名称
        "DEFAULT_VERSION":'v1',                                                    #设置默认版本
        "ALLOWED_VERSIONS":['v1','v2'] ,                                          #设置允许的版本
        # 设置为 None,没有通过认证的用户,request.user匿名用户 =None
        "UNAUTHENTICATED_USER":None,
        # 设置为UNAUTHENTICATED_TOKEN   tocken =None
        'UNAUTHENTICATED_TOKEN':None,
        #认证相关配置
         "DEFAULT_AUTHENTICATION_CLASSES":(
            "utils.auth.auth1.CustomAuthentication",
            ),
        #权限相关配置
        'DEFAULT_PERMISSION_CLASSES':[ "utils.permission.per1.CustomPermission"],
        #限制访问次数
        'DEFAULT_THROTTLE_RATES':{
            'anon':'5/m',  #匿名用户5次
            'user':'4/m',  #登录用户6次
        }
    
    }
    setings.py配置文件
    # 限制访问次数相关
    from rest_framework.throttling import BaseThrottle,AnonRateThrottle,SimpleRateThrottle
    #根据IP
    class CustomAnonThrottle(SimpleRateThrottle):
        # 代指配置文件中定义的访问频率的类 {'anon':'5/m'}
        scope = 'anon'
        def allow_request(self, request, view):
            # 已经登录管不着
            if request.user:
                return True
            # 获取当前访问用户的唯一标识 get_cache_key是抽象方法 ip+字符串格式化
            self.key = self.get_cache_key(request, view)
            # 根据用户的唯一标识 获取当我用户的所有访问记录
            self.history = self.cache.get(self.key, [])
            # 获取当前时间 [最近访问,最早访问  ]
            self.now = self.timer()
            # 做限制请求频率操作
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()  # 根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内
    
                # 根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
                # 超出合理长度 返回False,不能继续访问
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            # 访问记录列表在合理长度,return Ture可继续访问
            return self.throttle_success()
    
        def get_cache_key(self, request, view):
            return self.cache_format % {
                'scope': self.scope,
                'ident': self.get_ident(request)
            }
    
    #根据用户名
    class CustomUserThrottle(SimpleRateThrottle):
        #代指配置文件中定义的访问频率的类 {'anon':'5/m'}
        scope = 'user'
        def allow_request(self, request, view):
            #已经登录管不着
            if not request.user:
                return True
            #获取当前访问用户的唯一标识 用户名
            self.key = request.user.user
            #根据用户的唯一标识 获取当我用户的所有访问记录
            self.history = self.cache.get(self.key, [])
            #获取当前时间 [最近访问,最早访问  ]
            self.now = self.timer()
            #做限制请求频率操作
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()#根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内
    
           #根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
                # 超出合理长度 返回False,不能继续访问
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            #访问记录列表在合理长度,return Ture可继续访问
            return self.throttle_success()
    自定义限制请求频率类
    class UserGroupView(APIView):
        # permission_classes = []
        throttle_classes=[CustomAnonThrottle,CustomUserThrottle]
        def get(self, request, *args, **kwargs):
            # print(self.dispatch())
            print(request.user)
            # self.dispatch()
            # print(self.throttle_classes)
            # print(self.permission_classes)
            # print(request.user.user)
            # print(request.META.get('REMOTE_ADDR'))
            # print(request.META.get('HTTP_X_FORWARDED_FOR'))
            return Response('通过认证!')
    
    class TestView(APIView):
        permission_classes = []
        authentication_classes = []
        throttle_classes = [CustomAnonThrottle, CustomUserThrottle]
        def get(self, request, *args, **kwargs):
            return Response('test通过认证!')
    视图

    匿名用户使用IP作为用户唯一标识

    class CustomThrottle(SimpleRateThrottle):
        scope = 'anon'
        def get_cache_key(self, request, view):
            # if request.user.is_authenticated:
            #     return None  # Only throttle unauthenticated requests.
    
            return self.cache_format % {
                'scope': self.scope,
                'ident': self.get_ident(request)
            }
    自定义访问控制类
     #限制访问次数
        'DEFAULT_THROTTLE_RATES':{'anon':'5/m'}
    settings配置文件
    class UserGroupView(APIView):
        # permission_classes = []
        throttle_classes=[CustomThrottle]
        def get(self, request, *args, **kwargs):
            # print(request.user)
            # self.dispatch()
            # print(self.throttle_classes)
            # print(self.permission_classes)
            # print(request.user.user)
            return Response('通过认证!')
    视图

    匿名用户5/m

      url(r'^(?P<version>w+)/test/$', views.TestView.as_view(), name='xxxxx'), #匿名用户5/m
    路由
     #限制访问次数
        'DEFAULT_THROTTLE_RATES':{
            'anon':'5/m',  #匿名用户5次
            'user':'4/m',  #登录用户6次
        }
    setings.py配置文件
    #根据IP
    class CustomAnonThrottle(SimpleRateThrottle):
        # 代指配置文件中定义的访问频率的类 {'anon':'5/m'}
        scope = 'anon'
        def allow_request(self, request, view):
            # 已经登录管不着
            if request.user:
                return True
            # 获取当前访问用户的唯一标识 get_cache_key是抽象方法 ip+字符串格式化
            self.key = self.get_cache_key(request, view)
            # 根据用户的唯一标识 获取当我用户的所有访问记录
            self.history = self.cache.get(self.key, [])
            # 获取当前时间 [最近访问,最早访问  ]
            self.now = self.timer()
            # 做限制请求频率操作
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()  # 根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内
    
                # 根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
                # 超出合理长度 返回False,不能继续访问
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            # 访问记录列表在合理长度,return Ture可继续访问
            return self.throttle_success()
    
        def get_cache_key(self, request, view):
            return self.cache_format % {
                'scope': self.scope,
                'ident': self.get_ident(request)
            }
    自定义请求频率类
    class TestView(APIView):
        permission_classes = []
        authentication_classes = []
        throttle_classes = [CustomAnonThrottle, CustomUserThrottle]
        def get(self, request, *args, **kwargs):
            return Response('test通过认证!')
    视图

    登录用户4/m

     url(r'^(?P<version>w+)/usergroup/$', views.UserGroupView.as_view(), name='xxxxx'), #登录用户 4/m
    路由
    #限制访问次数
        'DEFAULT_THROTTLE_RATES':{
            'anon':'5/m',  #匿名用户5次
            'user':'4/m',  #登录用户6次
        }
    setings配置文件
    #根据用户名
    class CustomUserThrottle(SimpleRateThrottle):
        #代指配置文件中定义的访问频率的类 {'anon':'5/m'}
        scope = 'user'
        def allow_request(self, request, view):
            #已经登录管不着
            if not request.user:
                return True
            #获取当前访问用户的唯一标识 用户名
            self.key = request.user.user
            #根据用户的唯一标识 获取当我用户的所有访问记录
            self.history = self.cache.get(self.key, [])
            #获取当前时间 [最近访问,最早访问  ]
            self.now = self.timer()
            #做限制请求频率操作
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()#根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内
    
           #根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
                # 超出合理长度 返回False,不能继续访问
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            #访问记录列表在合理长度,return Ture可继续访问
            return self.throttle_success()
    自定义限制请求频率类
    class UserGroupView(APIView):
        # permission_classes = []
        throttle_classes=[CustomAnonThrottle,CustomUserThrottle]
        def get(self, request, *args, **kwargs):
            # print(self.dispatch())
            print(request.user)
            # self.dispatch()
            # print(self.throttle_classes)
            # print(self.permission_classes)
            # print(request.user.user)
            # print(request.META.get('REMOTE_ADDR'))
            # print(request.META.get('HTTP_X_FORWARDED_FOR'))
            return Response('通过认证!')
    View Code

    4.3、练习题

    要求:

    登录URL: 无权限限制,要求用户登录并返回token;

    首页URL:无权限限制,匿名用户限制 2/m, 登录用户 4/m ;

    订单URL: 匿名用户无法查看(权限) ,登录用户 4/m;

    from django.db import models
    
    from django.db import models
    
    class Userinfo(models.Model):
        user=models.CharField(max_length=32)
        pwd=models.CharField(max_length=64)
        email=models.CharField(max_length=64)
        user_type_choices=(
            (1,'普通用户'),
            (2,'文艺用户'),
            (3,'其他用户'),
        )
        user_type_id=models.IntegerField(choices=user_type_choices,default=1)
    
    #设置 one to one 1个用户不能在不同设备上登录
    #设置 Freikey 支持1个用户 在不同设备上同时登录
    class Token(models.Model):
        user=models.OneToOneField(Userinfo)
        token=models.CharField(max_length=64)
    数据库
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^login/',views.LoginView.as_view()),
        url(r'^index/',views.IndexView.as_view()),
        url(r'^order/',views.OrderView.as_view()),
    ]
    urls.py
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01.apps.App01Config',
        'rest_framework' #组册 rest_framework APP
    ]
    
    
    # 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK  里的东西
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
        "VERSION_PARAM":"version",                                                  #设置默认参数名称
        "DEFAULT_VERSION":'v1',                                                    #设置默认版本
        "ALLOWED_VERSIONS":['v1','v2'] ,                                          #设置允许的版本
        # 设置为 None,没有通过认证的用户,request.user匿名用户 =None
        "UNAUTHENTICATED_USER":None,
        # 设置为UNAUTHENTICATED_TOKEN   tocken =None
        'UNAUTHENTICATED_TOKEN':None,
        # 权限相关配置
        'DEFAULT_PERMISSION_CLASSES': ["utils.permission.permission1.CustomPermission"],
        #认证相关配置
         "DEFAULT_AUTHENTICATION_CLASSES":(
            "utils.auth.auth1.CustomAuthentication",
            ),
        #限制访问次数
        'DEFAULT_THROTTLE_RATES':{
            'anon':'2/m',  #匿名用户5次
            'user':'4/m',  #登录用户6次
        }
    
    }
    配置
    from rest_framework import exceptions
    from rest_framework.authentication import BasicAuthentication
    from app01 import models
    class CustomAuthentication(BasicAuthentication): #认证类
        def authenticate(self, request):
            #从 url的参数中获取客户端携带的tocken
            tocken=request.query_params.get('tocken')
            #如果是匿名用户 url中没有携带tocken返回None不抛出异常,表示运行匿名用户访问;
            if not tocken:
                return (None,None)
            #raise exceptions.AuthenticationFailed('认证失败') 抛出异常表示不允许匿名用户登录
            #去数据库中检查tocken是否存在?
            tocken_obj=models.Token.objects.filter(token=tocken).first()
            #如果没有在数据中查询到用户携带tocken也允许访问
            if not tocken_obj:
                return (None,None)
            #如果存在
            return (tocken_obj.user,tocken_obj)
    
    #如果自定义认证类的authenticate方法return了(None,None),没有raise异常代表允许匿名用户访问
    认证
    #权限相关
    from rest_framework.permissions import BasePermission,AllowAny
    from app01 import views
    class CustomPermission(BasePermission):
        message='匿名用户无权限访问,订单页面。' #定制错误信息
        def has_permission(self, request, view):
            #view代指那个视图
            #如果用户为普通用户,访问的视图是UserGroupView,没有权限拒绝访问
            if not request.user  and isinstance(view,views.OrderView) :
                return False #返回False 代表没有权限访问
            return True       # return True  #返回True 代表都有权限访问
    权限
    from django.shortcuts import render
    from utils.throttle.throttle1 import CustomAnonThrottle,CustomUserThrottle
    from rest_framework.views import APIView   #导入 APIView
    from rest_framework.authentication import BasicAuthentication
    from rest_framework.authentication import SessionAuthentication
    from app01 import models
    from rest_framework.request import Request
    from rest_framework.response import Response
    from django.http import JsonResponse
    
    #生成动态Tocken
    def gen_tcoken(username):
        import random,hashlib,time
        ctime=str(time.time())
        hash=hashlib.md5(username.encode('utf-8'))
        hash.update(ctime.encode('utf-8'))
        return hash.hexdigest()
    
    
    ##认证相关
    
    
    
    
    class LoginView(APIView):
        authentication_classes = []
        def get(self,request,*args,**kwargs):
            ret={'code':666,'token':None,'msg':None}
            username=request.GET.get('username')
            # 由于API对request进行二次封装GET请获取参数可以使用request.query_params.get('pwd')
            #由于API对request进行二次封装 POST获取参数可以使用request.data.get('pwd')
            pwd=request.query_params.get('pwd')
            user_obj=models.Userinfo.objects.filter(user=username,pwd=pwd).first()
            if user_obj:
                tk=gen_tcoken(username)
                models.Token.objects.update_or_create(user=user_obj, defaults={'token': tk})
                ret['token']=tk
            else:
                ret['code']=444
                ret['msg']='用户名或者密码错误'
            return JsonResponse(ret)
    
    
    
    
    class IndexView(APIView):
        # 如果在全局配置了权限认证,在单个authentication_classes = []代指本视图不做认证
        throttle_classes = [CustomAnonThrottle, CustomUserThrottle]
        def get(self,request,*args,**kwargs):
            return Response('通过主页认证')
    
    
    class OrderView(APIView):
        '''
        订单页面:用户登录之后才能访问,每分钟访问4次
        认证环节:允许匿名用户
        权限:不允许匿名用户对  访问 OrderView视图
        节流:4/m
    
        '''
        throttle_classes = [CustomUserThrottle]
        def get(self,request):
            return Response('通过订单页面认证')
    访问频率控制+视图

    5、解析器(parser)

    REST framework的解析器:根据客户端发送的content-type请求头, 选择对应的解析器, 对请求体内容进行处理。

    5.1源码逻辑

    a、首先把self.get_parsers(姑娘)和get_content_negotiator(劳保)都封装到request对象中;

     return Request(
                request,
                parsers=self.get_parsers(),
                negotiator=self.get_content_negotiator(),
            )
            
    View Code

    b、执行request.data调用解析功能negotiator根据 content_type挑选对应的parsers做解析工作

    5.2:限制API接口仅处理请求头中设置content-type/json 和content-type/form的请求体

    from rest_framework.parsers import JSONParser
    from rest_framework.parsers import FormParser
    from rest_framework.parsers import MultiPartParser
    from rest_framework.negotiation import DefaultContentNegotiation
    class ParserView(APIView):
        parser_classes=[JSONParser,FormParser]
        #只处理请求头中包含:Content - Type:application/jison和Content - Type:application/form
        def get(self,request,*args,**kwargs):
            return Response ('Parsing success')
        def post(self,request,*args,**kwargs):
            print(request.data)
            # self.dispatch()
            return Response('Parsing success')
    View Code

    接口测试:PostmanAPI接口测试工具

    6、framework序列化和用户请求数据验证

    后台通过Django ORM查询到数据都是QuerySet数据类型,这种数据类型不可以直接json.dumps()序列化响应给客户端,REST framework自带序列化功能,可以将数据库查询到的Foreign Key、Many to Many、Choice字段序列化为字典响应给客户端,还可以对用户提交的数据进行的验证(功能类似Form验证);

    序列化

    6.1:单表查询结果序列化

    #自定义序列化类
    from rest_framework import serializers
    
    class UserSerializers(serializers.Serializer):
        user=serializers.CharField()
        pwd=serializers.CharField()
        email=serializers.CharField()
        user_type_id=serializers.IntegerField()
    
    #应用
    class SerializeView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
            ser=UserSerializers(instance=user_list,many=True)
            return Response(ser.data)
    单表无任何外键关系的表

    6.2:source连表查询结果序列化(外键 1对多关系)

    #自定义序列化类
    from rest_framework import serializers
    
    class UserSerializers(serializers.Serializer):
        user=serializers.CharField()
        pwd=serializers.CharField()
        email=serializers.CharField()
        user_type_id=serializers.IntegerField()
        #source代指连表操作获取数据
        ug=serializers.CharField(source='ug.title')
    
    #应用
    class SerializeView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
            ser=UserSerializers(instance=user_list,many=True)
            return Response(ser.data)
    View Code

     正向连表 外键字段.xx

    class TestSerializer(serializers.ModelSerializer):
        #1对多字段
        house=serializers.CharField(source='House.addr')
        #1对多字段
        car=serializers.CharField(source='car.titile')
        #多对多字段
        madam=serializers.CharField(source='madam.all')
        car_level=serializers.CharField(source='car.get_car_level_display')
        class Meta:
            model=models.Person
            fields=['name','house','car','madam','car_level']
    外键字段.xx

    source还支持反向连表查询

    person=serializers.CharField(source='person_set.all')
    小写表名_set().all()

    depth 连表深度:自动连表,连表深度最大为10

    class UserSerializers(serializers.ModelSerializer):
        pwd=serializers.HyperlinkedIdentityField(view_name='xxxx')
        class Meta:
            model=models.Userinfo
            fields='__all__'
            depth=2 #自动联表
    View Code

    如果是数据库中普通字段在自定制serializers类中定义fileds=[‘字段名称’ ]属性即可,但是遇到choices(中文)、Many to Many字段怎么处理呢?

    获取choice字段中文: get_choice字段_display

    get_choices字段_display

    获取多对多字段数据

    方法1:

    CharField()中的source参数之所以支持 外键跨表、choice字段获取中文名、多对多字段获取,原理是因为CharField类中的2个方法:

    1、get_attribute 去数据库中获取值

    2、to_representation 在页面上显示值

    如果自己实现这2种方法,也可以自定制获取、显示外键、多对多、choice字段数据;

    class MyFielf(serializers.CharField):
        def get_attribute(self,instance):
            #instance 代表每一行
            madam_list=instance.madam.all()
            return madam_list
        def to_representation(self,value):
            #value 就是get_attribute return的结果
            ret=[]
            for row in value:
                ret.append({'name':row.name})
            return ret
    
    class TestSerializer(serializers.ModelSerializer):
        madams=MyFielf()
        class Meta:
            model=models.Person
            fields=['name','madams']
    View Code

    方法2:

    SerializerMethodField字段结合method_name=' 自定制方法'参数 

    class ModelCourseDetaiSerializer(serializers.ModelSerializer):
        course_name=serializers.CharField(source='course.name') #外键
        price_policy=serializers.SerializerMethodField(method_name='get_price_policyss')# 多对多
        recommend_courses=serializers.SerializerMethodField(method_name='get_recommend_courses_list') # ContentType
        teacher_list = serializers.SerializerMethodField(method_name='get_teacher_lists')
        class Meta:
            model=models.CourseDetail
            fields=['id','course_name','price_policy','recommend_courses','teacher_list']
        #推荐课程
        def get_recommend_courses_list(self,obj):
            ret=[]
            courses_list=obj.recommend_courses.all()
            for i in courses_list:
                ret.append({'id':i.id,'name':i.name})
            return ret
        #价格策略
        def get_price_policyss(self,obj):
            ret = []
            price=obj.course.price_policy.all()
            for i in price:
                ret.append({'id':i.id,'name':i.valid_period,'price':i.price})
            return ret
        #课程讲师
        def get_teacher_lists(self,obj):
            ret = []
            teachers=obj.teachers.all()
            for t in teachers:
                ret.append({'name':t.name,'brief ':t.brief,'role':t.get_role_display()})
            return ret
    
    class Course_DetailView(APIView):
        def get(self,*args,**kwargs):
            pk=kwargs.get('pk',None)
            detail=models.CourseDetail.objects.get(course_id=pk)
            ser = ModelCourseDetaiSerializer(instance=detail,many=False)
            return Response(ser.data)
    serializers.SerializerMethodField(method_name='自定制方法')

    对提交到API接口的数据做校验

    serializers不仅可以把后台ORM获取到的数据序列化后响应客户端,还可以对客户端提交的数据进行验证(类似Form验证功能);

    #自定义序列化类
    from rest_framework import serializers
    
    #复杂验证规则
    class PasswordValidator(object):
        def __init__(self, base):
            self.base = base
        def __call__(self, value):
            if value != self.base:
                message = '密码必须是 %s.' % self.base
                raise serializers.ValidationError(message)
    
    class UserSerializers(serializers.Serializer):
        user=serializers.CharField(error_messages={"required":"用户名不能为空"})
        #PasswordValidator验证类
        pwd=serializers.CharField(validators=[PasswordValidator(base='666')])
        email=serializers.CharField()
        user_type_id=serializers.IntegerField()
        #source代指连表操作获取数据
        ug=serializers.CharField(source='ug.title')
    
    #应用
    class SerializeView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
            ser=UserSerializers(instance=user_list,many=True)
            return Response(ser.data)
    
        def post(self, request, *args, **kwargs):
            #对用户提交数据进行验证 data=request.dat
            ser = UserSerializers(data=request.data)
            #验证成功返回 正确数据
            if ser.is_valid():
                return Response(ser.validated_data)
            else:
                #验证失败放回错误信息
                print(ser.errors)
                return Response(ser.errors)
    View Code

    serializers的数据验证功能类似Form验证,Form验证可以结合Models做ModelForm验证,serializers也是可以的;

    class UserSerializers(serializers.ModelSerializer):
        class Meta:
            model=models.Userinfo
            fields="__all__"
    
    
    
    
    #应用
    class SerializeView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
            ser=UserSerializers(instance=user_list,many=True)
            return Response(ser.data)
    View Code

    HyperlinkedIdentityField字段,(根据PK反向生成URL)

    """
    Django settings for RestFrameWor练习 project.
    
    Generated by 'django-admin startproject' using Django 1.11.4.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/1.11/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/1.11/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = '#zh*tb-b3=2w^lka#47nztp=-shjy0a1j^8&pqypcaug3d%6oq'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = []
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01.apps.App01Config',
        'rest_framework' #组册 rest_framework APP
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'RestFrameWor练习.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR,  'templates'),],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'RestFrameWor练习.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/1.11/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    
    STATIC_URL = '/static/'
    
    
    
    
    
    # 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK  里的东西
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
        "VERSION_PARAM":"version",                                                  #设置默认参数名称
        "DEFAULT_VERSION":'v1',                                                    #设置默认版本
        "ALLOWED_VERSIONS":['v1','v2'] ,                                          #设置允许的版本
        # 设置为 None,没有通过认证的用户,request.user匿名用户 =None
        "UNAUTHENTICATED_USER":None,
        # 设置为UNAUTHENTICATED_TOKEN   tocken =None
        'UNAUTHENTICATED_TOKEN':None,
        # 权限相关配置
        'DEFAULT_PERMISSION_CLASSES': ["utils.permission.permission1.CustomPermission"],
        #认证相关配置
         "DEFAULT_AUTHENTICATION_CLASSES":(
            "utils.auth.auth1.CustomAuthentication",
            ),
        #限制访问次数
        'DEFAULT_THROTTLE_RATES':{
            'anon':'2/m',  #匿名用户2次
            'user':'4/m',  #登录用户4次
        }
    
    }
    setings配置文件

    注意如果配置文件中配置了版本控制功能,在反向生成URL的时候也要体现;

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^login/',views.LoginView.as_view()),
        url(r'^index/',views.IndexView.as_view()),
        url(r'^order/',views.OrderView.as_view()),
        url(r'^parser/', views.ParserView.as_view()),
        url(r'^serialize/',views.SerializeView.as_view()),
        url(r'^serialize.(?P<format>w+)',views.SerializeView.as_view()),
        url(r'test/(?P<pk>d+)/(?P<version>[v1|v2]+)', views.SerializeView.as_view(),name='xxxx'),
    ]
    urls.py
    class UserSerializers(serializers.ModelSerializer):
        pwd=serializers.HyperlinkedIdentityField(view_name='xxxx')
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    #应用
    class SerializeView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
            ser=UserSerializers(instance=user_list,many=True,context={'request':request})
            return Response(ser.data)
    视图

    自动生成url字段

    class UserSerializers(serializers.HyperlinkedModelSerializer):
        class Meta:
            model=models.Userinfo
            fields=['id','user','pwd','email','url']
    
    
    #应用
    class SerializeView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
            ser=UserSerializers(instance=user_list,many=True,context={'request':request})
            return Response(ser.data)
    
        def post(self, request, *args, **kwargs):
            #对用户提交数据进行验证 data=request.dat
            ser = UserSerializers(data=request.data)
            #验证成功返回 正确数据
            if ser.is_valid():
                return Response(ser.validated_data)
            else:
                #验证失败放回错误信息
                print(ser.errors)
                return Response(ser.errors)
    View Code

    Serializer小结:

    Serializer虽然叫序列化,却有2大功能 (序列化、Form验证)3大父类(Serializer,ModelSerializer,HyperlinkedModelSerializer);

    自定义的serializers类,有3中可继承的父类;

    1、serializers.Serializer:手动指定需要序列化和验证的字段

    2、serializers.ModelSerializer:自动获取需要序列化和验证的字段(类似ModelFrom功能)

    3、serializers.HyperlinkedModelSerializer 自动生成超链接

    7、分页

    当用户向我们的REST framework提交查询请求时候,怎么能把数据库里的全部数据list出来响应给用户呢?这就涉及到数据分页了,不仅要分页还要考虑分页性能;

    a.根据 url参数携带的页码,进行分页;

    url(r'^pager/$', views.PagerView.as_view()),
    urls.py
    #分页相关配置
    from rest_framework.pagination import PageNumberPagination
    
    class ZhanggenPagination(PageNumberPagination):
        #默认一页显示多少条数据
        page_size=1
        #http://127.0.0.1:8008/pager/?page=1  url参数名称
        page_query_param='page'
        #http://127.0.0.1:8008/pager/?page=1&page_size=2,通过url传参定制一页显示多少条数据;
        page_size_query_param = 'page_size'
    
    
    #序列化相关配置
    class PagerSerializers(serializers.ModelSerializer):
        #新增一个字段
        zhanggen=serializers.CharField(source='ug.title')
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    
    #CBV应用分页和序列化功能
    class PagerView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #实例化 自定制分页类
            obj=ZhanggenPagination()
            #调用分页类的paginate_queryset方法,根据url的参数 获取分页数据;
            page_user_list=obj.paginate_queryset(user_list,request,self )
            #序列化
            ser=PagerSerializers(instance=page_user_list,many=True)
            #返回结果包页码"next": "http://127.0.0.1:8008/pager/?page=2",点击跳转到下一页;
            # respose=obj.get_paginated_response(ser.data)
            # return respose
            #返回结果不包含页码
            return Response(ser.data)
    视图

    b. 基于某位置偏移进行分页 offset limit

    urlpatterns = [url(r'^pager/$', views.PagerView.as_view()), ]
    urls.py
    #分页相关配置
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.pagination import LimitOffsetPagination
    from rest_framework.pagination import CursorPagination
    
    #http://127.0.0.1:8008/pager/?offset=2&limit=5 获取3-8条(移动到2,基于2偏移获取5条数据)
    class Zhanggen1Pagination(LimitOffsetPagination):
        # 默认每页显示的数据条数
        default_limit = 1
        # URL中传入的数据位置的参数(基于这个位置偏移)
        offset_query_param = 'offset'
        # 偏移量参数
        limit_query_param = 'limit'
        # 最大每页显得条数
        max_limit = None
    
    
    
    #序列化相关配置
    class PagerSerializers(serializers.ModelSerializer):
        #新增一个字段
        zhanggen=serializers.CharField(source='ug.title')
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    
    
    
    
    #CBV应用分页和序列化功能
    class PagerView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #实例化 自定制分页类
            obj=Zhanggen1Pagination()
            #调用分页类的paginate_queryset方法,根据url的参数 获取分页数据;
            page_user_list=obj.paginate_queryset(user_list,request,view=self )
            #序列化
            ser=PagerSerializers(instance=page_user_list,many=True)
            #返回结果包页码"next": "http://127.0.0.1:8008/pager/?page=2",点击跳转到下一页;
            respose=obj.get_paginated_response(ser.data)
            return respose
            #返回结果不包含页码
            # return Response(ser.data)
    视图

    c. 游标分页

    urlpatterns = [ url(r'^pager/$', views.PagerView.as_view()), ]
    urls.py
    #分页相关配置
    from rest_framework.pagination import CursorPagination
    #http://127.0.0.1:8008/pager/?cursor=cD0x
    class Zhanggen2Pagination(CursorPagination):
        # URL传入的游标参数
        cursor_query_param = 'cursor'
        # 默认每页显示的数据条数
        page_size = 1
        # URL传入的每页显示条数的参数
        page_size_query_param = 'page_size'
        # 每页显示数据最大条数
        max_page_size = 1000
        # 根据ID从大到小排列
        ordering = "id"
    
    
    #序列化相关配置
    class PagerSerializers(serializers.ModelSerializer):
        #新增一个字段
        zhanggen=serializers.CharField(source='ug.title')
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    
    
    #CBV应用分页和序列化功能
    class PagerView(APIView):
        def get(self,request,*args,**kwargs):
            user_list=models.Userinfo.objects.all()
            #实例化 自定制分页类
            obj=Zhanggen2Pagination()
            #调用分页类的paginate_queryset方法,根据url的参数 获取分页数据;
            page_user_list=obj.paginate_queryset(user_list,request,view=self )
            #序列化
            ser=PagerSerializers(instance=page_user_list,many=True)
            #返回结果包页码"next": "http://127.0.0.1:8008/pager/?page=2",点击跳转到下一页;
            respose=obj.get_paginated_response(ser.data)
            return respose
            #返回结果不包含页码
            # return Response(ser.data)
    视图

    分页功能小结:

    a. REST framework一共有3中分页方式

    1、根据页码和每次能查看的条目数:http://127.0.0.1:8008/pager/?page=1&page_size=2 获取1-2条

    2、基于offset偏移位置做limit获取:http://127.0.0.1:8008/pager/?offset=2&limit=5 获取3-8条(移动到2,基于2偏移获取5条数据)

    3、游标分页(加密页面,1页1页得翻页)

    b. 浅谈分页性能

    分页方式1和方式2的缺陷:

    以上分页方式最终转化成SQL语句本质是  offset和 limit

    第1页:select * from 表 offset  0  limit 5

    第2页:select * from 表 offset  5  limit 5

    第3页:select * from 表 offset  10  limit 5

    .................................

    第N页:select * from 表 offset  X  limit 5

    越往后翻 offset的值就会越大,已经翻阅过的数据量越多,需要扫描的数据就会越多,每次翻页数据库查询会把已经翻阅的数据的从头开始再扫描一遍,速度就会越慢;

    解决方案:

    游标分页之所以会把页码加密,只让用户点击上一页和下一页,进行1页1页的翻页。。。

    原因:普通翻页和直接跳转到某页会涉及全表扫描,除非 查询数据库SQL语句中包含      id < n  , id >n  

    翻页时记住当前页最大ID和最小ID,想要到上一页 id>min, 想要到下一页 id>max    select * from 表  where id >n  offset  0  limit 5

    缺点:无法直接跳转到某一页

     8、路由系统和视图

    REST framework根据配置自动生成 增、删、改、list的url并且自动生成视图中的list、add、delete、put方法;解放你的双手!

    自建路由

    urlpatterns = [
    #路由相关
        #http://127.0.0.1:8008/router/
        # 支持:GET 查询显示list页面 、 POST:增加
        url(r'^router/$', views.RouterView.as_view()),
        #http://127.0.0.1:8008/router.json
        #支持:携带.json后缀
        url(r'^router.(?P<format>w+)$', views.RouterView.as_view()),
        #http://127.0.0.1:8008/router/1/
        # 支持:put修改、delete 删除
        url(r'^router/(?P<pk>d+)/$', views.RouterView.as_view()),
        #http://127.0.0.1:8008/router/1.json
        #支持 get 获取单条数据
        url(r'^router/(?P<pk>d+).(?P<format>w+)$', views.RouterView.as_view()),
    
    ]
    urls.py
    #路由相关
    #路由序列化相关
    class RouteSerializers(serializers.ModelSerializer):
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    
    #应用
    class RouterView(APIView):
        def get(self,*args,**kwargs):
            pk=kwargs.get('pk')
            if pk:
                user_obj= models.Userinfo.objects.get(pk=pk)
                ser=RouteSerializers(instance=user_obj,many=False)
            else:
                user_list=models.Userinfo.objects.all()
                ser = RouteSerializers(instance=user_list, many=True)
            return Response(ser.data)
    views视图

    GenericAPIView内置了一些方法 完成 分页、序列化功能,无需自己实例化对象了,路由没作任何改变;

    from app01 import views
    urlpatterns = [
    
        #   半自动创建路由
        #http://127.0.0.1:8008/router/
        # 支持:GET 查询显示list页面 、 POST:增加
        url(r'^router/$', views.RouterView.as_view()),
        #http://127.0.0.1:8008/router.json
        #支持:携带.json后缀
        url(r'^router.(?P<format>w+)$', views.RouterView.as_view()),
        #http://127.0.0.1:8008/router/1/
        # 支持:put修改、delete 删除
        url(r'^router/(?P<pk>d+)/$', views.RouterView.as_view()),
        #http://127.0.0.1:8008/router/1.json
        #支持 get 获取单条数据
        url(r'^router/(?P<pk>d+).(?P<format>w+)$', views.RouterView.as_view()),
    
    
    ]
    urls.py
    #路由序列化相关
    from rest_framework import serializers
    class RouteSerializers(serializers.ModelSerializer):
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    
    #路由分页相关类
    from rest_framework.pagination import PageNumberPagination
    class ZhanggenPagination(PageNumberPagination):
        #默认一页显示多少条数据
        page_size=1
        #http://127.0.0.1:8008/pager/?page=1  url参数名称
        page_query_param='page'
        #http://127.0.0.1:8008/pager/?page=1&page_size=2,通过url传参定制一页显示多少条数据;
        page_size_query_param = 'page_size'
    
    
    #半自动路由
    
    from rest_framework.generics import GenericAPIView
    class RouterView(GenericAPIView):
        #数据库数据
        queryset = models.Userinfo.objects.all()
        #序列化类
        serializer_class = RouteSerializers
        #分页类
        pagination_class = ZhanggenPagination
        def get(self,request,*args,**kwargs):
            #获取数据库数据
            user_list=self.get_queryset()
            #获取分页相关,对数获取到的数据进行分页
            page_user_list=self.paginate_queryset(user_list)
            #获取序列化相关,对已经分页得数据库数据进行序列化
            ser=self.get_serializer(instance=page_user_list,many=True)
            respose=self.get_paginated_response(ser.data)
            return respose
    views.py

    半自动路由: as_view({'get': "retrieve", "put": 'update','delete':'destroy'})) 更加as.view方法中的参数,自动触发视图执行

    from app01 import views
    urlpatterns = [  
      #半自动路由:根据不同的请求自动触发 不同的视图做不同的操作
        url(r'^router/$',views.RouterView.as_view({'get':"list","post":'create' } )),
        #http://127.0.0.1:8008/router/1/
        #get请求获取某条数据  put请求某条数据  delete请求删除单条数据
        url(r'^router/(?P<pk>d+)/$', views.RouterView.as_view({'get': "retrieve", "put": 'update','delete':'destroy'})),
    ]
    urls.py
    #路由序列化相关
    from rest_framework import serializers
    class RouteSerializers(serializers.ModelSerializer):
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    from rest_framework.viewsets import ModelViewSet
    class RouterView(ModelViewSet):
        #数据库数据
        queryset = models.Userinfo.objects.all()
        #序列化类
        serializer_class = RouteSerializers
    视图

    全自动路由

    from django.conf.urls import url,include
    from app01 import views
    from rest_framework.routers import DefaultRouter
    
    router=DefaultRouter()
    #http://127.0.0.1:8008/zhanggen/2/  设置url前缀对应的视图
    router.register('zhanggen',views.RouterView)
    
    urlpatterns = [
    
      url(r'^',include(router.urls)),
    
    ]
    urls.py
    #路由序列化相关
    class RouteSerializers(serializers.ModelSerializer):
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    
    from rest_framework.viewsets import ModelViewSet
    from rest_framework import mixins
    class RouterView(ModelViewSet):
        #数据库数据
        queryset = models.Userinfo.objects.all()
        #序列化类
        serializer_class = RouteSerializers
    视图

    framework视图类总结

    继承最底层APIView:功能少,可定制性强

    继承ModelViewSet:功能丰富,FBV里list、add、delet..方法可定制性查;

     9、渲染

    REST framework根据用户请求url后缀(http://127.0.0.1:8008/render.json,http://127.0.0.1:8008/render/form/)的不同,渲染出不同的数据格式响应给客户端;

    注意:后端渲染功能是结合路由系统中设置format传入的参数来做渲染的,所有不要忘了在路由系统中设置format来接收url传来的参数;

    urlpatterns = [
    
         #渲染相关
        url(r'^render/$', views.RenderView.as_view()),
        #http://127.0.0.1:8008/render.json
        url(r'^render.(?P<format>w+)$', views.RenderView.as_view()),
        #http://127.0.0.1:8008/render/json/
        url(r'^render/(?P<format>w+)/$', views.RenderView.as_view()),   
    
    ]
    路由系统
    #渲染相关
    from rest_framework.renderers import JSONRenderer,AdminRenderer,BrowsableAPIRenderer,HTMLFormRenderer
    #序列化
    class RenderSerializers(serializers.ModelSerializer):
        class Meta:
            model=models.Userinfo
            fields='__all__'
    
    # class RenderView(APIView):
    #     #支持响应 json、admin 格式的数据
    #     renderer_classes =[JSONRenderer,AdminRenderer,BrowsableAPIRenderer]
    #     def get(self,request,*args,**kwargs):
    #         user_list=models.Userinfo.objects.all()
    #         ser=RenderSerializers(instance=user_list,many=True)
    #         return Response(ser.data)
    
    class RenderView(APIView):
        #支持响应form格式的数据
        renderer_classes =[HTMLFormRenderer]
        def get(self,request,*args,**kwargs):
            #注意返回form格式的数据instance必须为单个对象
            user_list=models.Userinfo.objects.all().first()
            ser=RenderSerializers(instance=user_list,many=False)
            return Response(ser.data)
    视图

    使用RET framework开展项目总结

    0、RET framework 内置了 版本、用户认证、访问权限、限制访问频率公共功能;(全局使用在配置文件配置,在全局使用的前提下,局部视图使用 xxclass=[ ]代表局部不使用)

    1、虽然使用全自动url结合ModelViewSet视图可以快速搭建一个简单的API,但是慎用因为后期项目扩展和更新很难修改;

    class Zhanggen(APIView): #继承APIView可扩展性强
        def get(self, request, *args, **kwargs):
          pass
    
        def post(self, request, *args, **kwargs):
            pass
    
        def put(self, request, *args, **kwargs):
            pass
    
        def delete(self, request, *args, **kwargs):
            pass
    View Code

    2、1个视图对应4个标准url

        #http://127.0.0.1:8008/router/
        # 支持:GET 查询显示list页面 、 POST:增加
        url(r'^router/$',views.RouterView.as_view()),
        #http://127.0.0.1:8008/router.json
        #支持:携带.json后缀
        url(r'^router.(?P<format>w+)$', views.RouterView.as_view()),
        #http://127.0.0.1:8008/router/1/
        # 支持:put修改、delete 删除
        url(r'^router/(?P<pk>d+)/$', views.RouterView.as_view()),
        #http://127.0.0.1:8008/router/1.json
        #支持 get 获取单条数据
        url(r'^router/(?P<pk>d+).(?P<format>w+)$', views.RouterView.as_view()),
    View Code

    3、版本控制

    url(r'test/(?P<pk>d+)/(?P<version>[v1|v2]+)$', views.SerializeView.as_view(),name='xxxx'),
    View Code

    银角大王博客:http://www.cnblogs.com/wupeiqi/articles/7805382.html

  • 相关阅读:
    正整数分解质因数
    水仙花数
    键入任意整数,将之从小到大输出
    有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少
    输入某年某月某日,判断这一天是这一年的第几天?
    java 日期增加
    oracle数据库 ORA-01461: can bind a LONG value only for insert into a LONG column解决方案
    JAVA实现图片叠加效果
    JAVA_GET请求URL
    sqlserver-触发器-判断更新了哪个字段。
  • 原文地址:https://www.cnblogs.com/sss4/p/7874182.html
Copyright © 2020-2023  润新知