当前位置:   article > 正文

Django之REST framework源码分析

django restful 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:\virtualenv\ven
Installing setuptools, pip, wheel...done.

4、激活虚拟环境
linux
source venv/bin/activate

DOS
cdvirtualenv\venv1\Scripts
activate

安装Django
>>> exit()
(venv1) D:\virtualenv\venv1\Scripts>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

转载于:https://www.cnblogs.com/sss4/p/7874182.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/71613
推荐阅读
相关标签
  

闽ICP备14008679号