Django之REST framework源碼分析

前言:

 

Django REST framework,是1個基於Django搭建 REST風格API的框架;html

一、什麼是API呢?前端

API就是訪問便可獲取數據的url地址,下面是一個最簡單的 Django API,訪問http://127.0.0.1:8000/,返回用戶列表;python

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)
視圖

 

二、什麼是restfunAPI呢?linux

若是新增增長用戶功能,再向用戶暴露1個API接口  http://127.0.0.1:8000/useradd/?new_user='A'web

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接口sql

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規範;django

在1個視圖中經過判斷用戶的請求method不一樣(get/post/put/delete),後臺作不一樣的操做;這樣就能夠只暴露1個API給用戶而且維護了代碼的整潔,這就是restful 規範之一;json

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有不少規範,接下來咱們介紹一下它都有哪些規範?

三、REST framework 請求的生命週期

 

 

 

1、RESTful API設計規範 

RESTful API的設計須要遵循不少規範,可是隻是建議,若是不遵照這種規範也是能夠的,如下是 它部分規範介紹;

 

一、面向資源

面向資源指得是把網絡中能夠訪問到的任何url都想象成1個資源,這個資源能夠是1個文件、 1張圖片、1個視頻....。

例如:

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

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

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

 注意 1級域名後邊(videos、files、pictures)都是名詞

 

二、版本號

咱們的API不是一成不變的,若是版本迭代也須要體如今url上,以供用戶選擇不一樣版本;

例如:

http://www.le.com/v1/files/  訪問版本1API資源

http://www.le.com/v2/files/  訪問版本2API資源

 

 三、返回值

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

經常使用狀態碼列表
狀態碼列表

 

 

四、API與用戶的通訊協議,老是使用https協議

 

五、域名規範

例如:

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

後端Django:https://api.le.com   注意會出現跨域問題

 

 

六、過濾,經過在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:指定篩選條件

 

 

七、返回結果: 根據用戶不一樣的操做,服務器放過的結果應該符合如下規範

GET /collection:返回資源對象的列表(數組)
GET /collection/resource:返回單個資源對象
POST /collection:返回新生成的資源對象
PUT /collection/resource:返回完整的資源對象
PATCH /collection/resource:返回完整的資源對象
DELETE /collection/resource:返回一個空文檔
View Code

 

八、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
#二、request被更換爲封裝完畢的request
        self.headers = self.default_response_headers  # deprecate?
#三、執行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、路由系統
三、CBV/FBV(視圖)
4、執行dispath方法
二次封裝request(原request,解析用戶請求的數據,authenticators=認證相關, 

negotiator=選擇相關, parser_context='封裝的self和參數')


try  
   獲取版本信息
   認證
   權限
   節流
   respose=執行 GET/POST/PUT/DELETE方法

except
   respose = 異常

finally
   處理響應內容
View Code

 

一、版本號設置

咱們的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('...')
視圖

 

 

二、用戶身份認證

根據用戶請求(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:
            #二、執行認證功能
            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
一、把 [認證對象,認證對象]封裝到Request
#二、調用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
二、調用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認證功能小結

 

一、建立2張表userinfo 和token表

二、認證類的authenticate方法去請求頭中獲取token信息,而後去token表中查詢token是否存在;

三、查詢到token 是正經常使用戶(返回 用戶名)不然爲匿名用戶(raise異常終止認證、或者 return none進行下一個認證)

四、局部應用

方式1::哪一個CBV須要認證在類中定義authentication_classes =[CustomAuthentication ] 

方式2:額外定義1個類,CBV多繼承

方式3:全局配置使用認證功能,那個CBV不使用authentication_classes =[ ]  

五、全局使用 在配置文件中配置 ,注意從新建立一個模塊,把認證類放裏面;

 

三、權限相關

社會三、六、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工做在中間件; 

 

 

四、限制用戶訪問頻率

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.一、局部應用(基於用戶對單個視圖進行訪問頻率限制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.一、全局應用(基於用戶對每一個視圖進行訪問頻率限制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.三、練習題

要求:

登陸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('經過訂單頁面認證')
訪問頻率控制+視圖

 

 

五、解析器(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接口測試工具

 

 

六、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個方法:

一、get_attribute 去數據庫中獲取值

二、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中可繼承的父類;

一、serializers.Serializer:手動指定須要序列化和驗證的字段

二、serializers.ModelSerializer:自動獲取須要序列化和驗證的字段(相似ModelFrom功能)

三、serializers.HyperlinkedModelSerializer 自動生成超連接

 

 

 

七、分頁

當用戶向咱們的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中分頁方式

一、根據頁碼和每次能查看的條目數:http://127.0.0.1:8008/pager/?page=1&page_size=2 獲取1-2條

 

二、基於offset偏移位置作limit獲取:http://127.0.0.1:8008/pager/?offset=2&limit=5 獲取3-8條(移動到2,基於2偏移獲取5條數據)

 

三、遊標分頁(加密頁面,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

缺點:沒法直接跳轉到某一頁

 

 

 

 八、路由系統和視圖

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..方法可定製性查;

 

 

 

 九、渲染

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=[ ]表明局部不使用)

一、雖然使用全自動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

二、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

三、版本控制

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

相關文章
相關標籤/搜索