Django rest-framework框架學習

  • REST與技術無關,表明的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯爲「表徵狀態轉移」
  • REST從資源的角度類審視整個網絡,它將分佈在網絡中某個節點的資源經過URL進行標識,客戶端應用經過URL來獲取資源的表徵,得到這些表徵導致這些應用轉變狀態
  • REST與技術無關,表明的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯爲「表徵狀態轉移」
  • 全部的數據,不管是經過網絡獲取的仍是操做(增刪改查)的數據,都是資源,將一切數據視爲資源是REST區別與其餘架構風格的最本質屬性
  • 對於REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture)

  REST是什麼呢?它是一種架構風格,騰訊公司或其餘公司創建API時要遵照的一種規則/風格,固然也有其餘規則能夠用。html

  那麼何爲REST架構風格呢?首先咱們來講說Web,由於rest是以web爲平臺的。咱們知道,web是分佈式信息系統爲超文本文件和其餘對象(資源)提供訪問入口。前端

  在web上訪問一個資源,須要3點:標識,表示,交互。經過這三個操做,又引出了三個概念:uri(包括url和urn),用來識別資源;representation(例如,圖片,html,媒體)用來表示資源;經過協議與資源進行交互。因此,REST就是經過使用HTTP協議和URI,利用client/server對資源進行CRUD操做。python

  那麼爲何要使用REST設計呢?確定是有它的優勢的。git

  1.客戶端-服務端分離github

   優勢:提升用戶界面的便攜性,經過簡化服務器提升可伸縮性....web

  2..無狀態(Stateless):從客戶端的每一個請求要包含服務器所須要的全部信息django

    優勢:提升可見性(能夠單獨考慮每一個請求),提升了可靠性(更容易從局部故障中修復),提升可擴展性(下降了服務器資源使用)json

有狀態與無狀態區別:
    如查詢員工工資,若是查詢工資是須要登錄系統,進入查詢工資的頁面,執行相關操做,獲取工資的多少,則這種狀況下是有狀態的,由於查詢工資的每一步操做都依賴於前一步操做,只須要前面操做不成功,後序操做就沒法執行;若是輸入一個url便可獲得指定員工的工資,則這種狀況下是無狀態的,由於獲取員工工資不依賴於其餘資源或者狀態,且這種狀況下,員工工資是一個資源,由一個url與其對應,能夠經過HTTP的GET方法獲得資源。
有狀態與無狀態區別

  3.緩存(Cachable):服務器返回信息必須被標記是否能夠緩存,若是緩存,客戶端可能會重用以前的信息發送請求api

   優勢:減小交互次數,減小交互的平均延遲跨域

  4.統一接口

   優勢:提升交互的可見性,鼓勵單獨改善組件

  5.支持按需代碼(Code-On-Demand 可選)

  優勢:提升可擴展性

  下面來解釋一下何爲表徵狀態轉移:

  舉個例子:例如我訂閱了一我的的博客,想要獲取他發表的全部文章(這裏『他發表的全部文章』就是一個資源Resource)。因而我就向他的服務發出請求,說『我要獲取你發表的全部文章,最好是atom格式的』,這時候服務器向你返回了atom格式的文章列表第一頁(這裏『atom格式的文章列表』就是表徵Representation)。

  你看到了第一頁的頁尾,想要看第二頁,這時候有趣的事情就來了。若是服務器記錄了應用的狀態(stateful),那麼你只要向服務詢問『我要看下一頁』,那麼服務器天然就會返回第二頁。相似的,若是你當前在第二頁,想服務器請求『我要看下一頁』,那就會獲得第三頁。可是REST的服務器偏偏是無狀態的(stateless),服務器並無保持你當前處於第幾頁,也就沒法響應『下一頁』這種具備狀態性質的請求。所以客戶端須要去維護當前應用的狀態(application state),也就是『如何獲取下一頁資源』。固然,『下一頁資源』的業務邏輯必然是由服務端來提供。服務器在文章列表的atom表徵中加入一個URI超連接(hyper link),指向下一頁文章列表對應的資源。客戶端就可使用統一接口(Uniform Interface)的方式,從這個URI中獲取到他想要的下一頁文章列表資源。上面的『可以進入下一頁』就是應用的狀態(State)。服務器把『可以進入下一頁』這個狀態以atom表徵形式傳輸(Transfer)給客戶端就是表徵狀態傳輸(REpresentational State Transfer)這個概念。

  REST是面向資源進行的,而資源是經過URI進行暴露的。

  URI 的設計只要負責把資源經過合理方式暴露出來就能夠了。對資源的操做與它無關,操做是經過 HTTP動詞來體現,因此REST 經過 URI 暴露資源時,會強調不要在 URI 中出現動詞。

例如:好比:左邊是錯誤的設計,而右邊是正確的

GET /rest/api/getDogs --> GET /rest/api/dogs 獲取全部小狗狗 
GET /rest/api/addDogs --> POST /rest/api/dogs 添加一個小狗狗 
GET /rest/api/editDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一個小狗狗 
GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 刪除一個小狗狗

REST很好利用了HTTP自己就有的一些特徵,如HTTP動詞,HTTP狀態碼,HTTP報頭等等。

更多參考:https://github.com/aisuhua/restful-api-design-references

注:上面的解釋是看見比較好的博客copy下來的,

RESTful API設計

API與用戶的通訊協議,老是使用HTTPs協議

域名

https://api.example.com     儘可能將API部署在專用域名(會存在跨域問題)

https://example.org/api/      API很簡單

 版本

URL,如:https://api.example.com/v1

請求頭

跨域時,引起發送屢次請求

路徑

視網絡上任何東西都是資源,均使用名詞表示(可複數)
  https://api.example.com/v1/zoos
  https://api.example.com/v1/animals
  https://api.example.com/v1/employees

method

  • GET :      從服務器取出資源(一項或多項)
  • POST :   在服務器新建一個資源
  • PUT :      在服務器更新資源(客戶端提供改變後的完整資源)
  • PATCH :  在服務器更新資源(客戶端提供改變的屬性)
  • DELETE :從服務器刪除資源

過濾

經過在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

狀態碼

  • 200 OK - [GET]:服務器成功返回用戶請求的數據,該操做是冪等的(Idempotent)。
  • 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
  • 202 Accepted - [*]:表示一個請求已經進入後臺排隊(異步任務)
  • 204 NO CONTENT - [DELETE]:用戶刪除數據成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操做,該操做是冪等的。
  • 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
  • 403 Forbidden - [*] 表示用戶獲得受權(與401錯誤相對),可是訪問是被禁止的。
  • 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操做,該操做是冪等的。
  • 406 Not Acceptable - [GET]:用戶請求的格式不可得(好比用戶請求JSON格式,可是隻有XML格式)。
  • 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再獲得的。
  • 422 Unprocesable entity - [POST/PUT/PATCH] 當建立一個對象時,發生一個驗證錯誤。
  • 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將沒法判斷髮出的請求是否成功。

更多狀態碼請看 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

錯誤處理

狀態碼是4xx時,應返回錯誤信息,error當作key。

{ 
  error: 「Invalid API key」 
}

 

返回結果

針對不一樣操做,服務器向用戶返回的結果應該符合如下規範。

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

Hypermedia API

RESTful API最好作到Hypermedia,即返回結果中提供連接,連向其餘API方法,使得用戶不查文檔,也知道下一步應該作什麼。 

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

 基於Django rest-framework框架的實現

先簡單建立一個Django項目而且使用pip install djangorestframwork 安裝django rest-framwork

要想快速實現的話能夠點擊這個快速實例

 序列化

在開始rest-framework說使用以前咱們先說一說序列化

開發咱們的Web API的第一件事是爲咱們的Web API提供一種將代碼片斷實例序列化和反序列化爲諸如json之類的表示形式的方式。咱們能夠經過聲明與Django forms很是類似的序列化器(serializers)來實現。

models部分:

from django.db import models


# Create your models here.
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    pub_date = models.DateField()
    publish = models.ForeignKey("Publish")
    authors = models.ManyToManyField("Author")

    def __str__(self):
        return self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    def __str__(self):
        return self.name

路由系統

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^books/', views.bookView.as_view()),
]

CBV視圖(views)部分:

from django.shortcuts import render, HttpResponse
from app01.models import *
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
# 爲queryset,model對象作序列化===》至關於form組件使用
# 這個能夠放在一個單獨的.py文件中
class BookSerializers(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()
    # 上面的只是針對的一對一字段的,若果用在一對多字段上的時候就會輸出關聯那張表的 def __str__(self)
    # publish = serializers.CharField()
    # 這個表示的是實現關聯表中publish表中的name字段,能夠本身定製
    publish = serializers.CharField(source="publish.name")
    # authors=serializers.CharField(source="authors.all")
    # 當出現多對多的時候上面的方式也不可行,上面會顯示的是一個queryset對象
    # 本身定製顯示什麼
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp


# 這個至關於modelform組件同樣
class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
        depth = 1

    # 當遇到一對多字段的時候
    publish = serializers.CharField(source='publish.name')
    # 多對多字段
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp


# Create your views here.
class BookViewSet(APIView):
    def get(self, request, *args, **kwargs):
        # 序列化
        # 方式一:
        # book_data = list(Book.objects.all().values('name', 'email'))
        # return HttpResponse(book_data)
        # 方式二:
        # from django.forms.models import model_to_dict
        # book_data = Book.objects.all()
        # temp = []
        # for obj in book_data:
        #     temp.append(model_to_dict(obj))
        # return HttpResponse(temp)
        # 方式三:
        # from django.core import serializers
        # book_data = Book.objects.all()
        # ret = serializers.serialize("json", book_data)
        # print(type(ret))
        # return HttpResponse(ret)
        # 方式四:序列組件
        # 這裏面和Django裏面的form組件和modelform組件類似
        # 這裏記住要是使用瀏覽器訪問的話這個必需要在setting中的INSTALLED_APPS註冊rest_framework要不就會報錯
        # 最好在項目一開始的時候就在setting裏面註冊
        book_data = Book.objects.all()
        # many=True 表示的queryset對象,反之many=False就表示爲model對象
        # 至關於form組件
        # bs = BookSerializers(book_data, many=True)
        bs = BookModelSerializers(book_data, many=True)
        return Response(bs.data)

記住要把在setting裏面註冊restframework

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'
]

serializers

在app01/utils/serializers.py 寫serializers代碼(相似於form)

from rest_framework import serializers


# 爲queryset,model對象作序列化===》至關於form組件使用
class BookSerializers(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()
    # 上面的只是針對的一對一字段的,若果用在一對多字段上的時候就會輸出關聯那張表的 def __str__(self)
    # publish = serializers.CharField()
    # 這個表示的是實現關聯表中publish表中的name字段,能夠本身定製
    publish = serializers.CharField(source="publish.name")
    # authors=serializers.CharField(source="authors.all")
    # 當出現多對多的時候上面的方式也不可行,上面會顯示的是一個queryset對象
    # 本身定製顯示什麼
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp

 ModelSerializer

在app01/utils/serializers.py 寫ModelSerializer代碼(相似於modelform)

from app01.models import *   # 導入表
from rest_framework import serializers

# 這個至關於modelform組件同樣
class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
    # 當遇到一對多字段的時候
    publish = serializers.CharField(source='publish.name')
    # 多對多字段
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp

當一個model有外鍵的時候,默認顯示的是外鍵的id,此時要顯示外鍵的全部值能夠用下面,depth,會把外鍵的全部值顯示出來,depth應該是整數,代表嵌套的層級數量。

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
        # 上面表示顯示全部字段也能夠只顯示咱們想要的
        # fields = ('id',) ,表示只顯示id
        # 要是不想顯示哪一個字段就可使用
        # exclude=('id',), 表示出了id其餘的都顯示
        depth = 1

上面就是關於get請求獲取全部的數據。

一樣 ModelSerializer也支持全局鉤子和局部鉤子,和form組件同樣局部鉤子爲validate_字段名(form組件爲clean_字段名),

全局鉤子爲validate。

 

提交post請求

路由系統

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^books/', views.BookViewSet.as_view(), name="book"),
]

在app01/utils/serializers.py 裏面的代碼

 
 
from rest_framework import serializers
from app01.models import *
class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

    publish = serializers.HyperlinkedIdentityField(
        view_name="detail_publish",
        lookup_field="publish_id",
        lookup_url_kwarg="pk",
    )

    # 當遇到一對多字段的時候
    # source='publish.name'這個後面是publish.name因此咱們前面傳回來的應該是publish.name對應的名字
    # # 若是是publish.pk 前面傳過來的就是 publish.pk對應的id
    publish = serializers.CharField(source='publish.name')
    # 多對多字段
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp

    # 由於上面本身定製了publish原來的save不支持這樣保存,因此下面要重寫create方法
    def create(self, validated_data):
        print(validated_data)
        # 若是上面定製的publish.name 改成 publish.pk下面這2行就不用寫了
        if not validated_data["publish"]["name"].isdigit():
            publish_id = Book.objects.filter(publish__name=validated_data["publish"]["name"]).values('publish_id').first()
            validated_data['publish_id'] = publish_id['publish_id']
        else:
            validated_data['publish_id']=validated_data["publish"]["name"]
        # 其實前端通常都會傳數字不會傳漢字, 因此能夠向下面這樣寫
        # validated_data['publish_id'] = validated_data["publish"]["name"]
        validated_data.pop('publish')
        authors = validated_data.pop('authors')
        book = Book.objects.create(**validated_data)
        book.authors.add(*authors)
        return book
    #
    def update(self, instance, validated_data):
        print('validated_data', validated_data)
        if not validated_data["publish"]["name"].isdigit():
            publish_id = Book.objects.filter(publish__name=validated_data["publish"]["name"]).values('publish_id').first()
            validated_data['publish_id'] = publish_id['publish_id']
        else:
            validated_data['publish_id']=validated_data["publish"]["name"]
        validated_data.pop('publish')
        authors = validated_data.pop('authors')
        instance.update(**validated_data)  # 更新普通字段和一對多字段
        nid = instance.values('id')[0]['id']
        instance.first().authors.set(authors)  # 更新多對多字段
        book_data = Book.objects.filter(id=nid).first()

        # 使用下面的放法前面傳進來的instance是obj對象,而這裏傳進來的queryset對象像下面出入同樣
        # book_obj = Book.objects.filter(pk=pk).first()
        # bs = BookModelSerializers(book_obj, data=request.data)
        # instance.authors.set(authors)
        # book_data = super().update(instance, validated_data
        return book_data

CBV視圖(view)部分

from django.shortcuts import render, HttpResponse
from app01.models import *
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
# 把Serializers和ModelSerializers單獨放在一個py文件中
from app01.utils.serilizer import *
import json
# Create your views here.
class BookViewSet(APIView):
    def get(self, request, *args, **kwargs):
        book_data = Book.objects.all()
        # many=True 表示的queryset對象,反之many=False就表示爲model對象
        # 至關於form組件
        # bs = BookSerializers(book_data, many=True)
        # 這個就是至關於modelform組件,
        bs = BookModelSerializers(book_data, many=True)
        return Response(bs.data)

    def post(self, request, *args, **kwargs):
        bs = BookModelSerializers(data=request.data)
        if bs.is_valid():
            # 直接把這個傳到後面就不須要作驗證了,對於多對多關係提交自定製顯示
            bs.save(authors=request.data['authors'])
            return Response(bs.data)
        else:
            return Response(bs.errors)


class BookDetailViewSet(APIView):
    def get(self, request, pk):
        book_obj = Book.objects.filter(pk=pk).first()
        bs = BookModelSerializers(book_obj, context={'request': request})
        return Response(bs.data)

    def put(self, request, pk):
        book_obj = Book.objects.filter(pk=pk)
        # 對應serilizer裏面的
        # book_obj = Book.objects.filter(pk=pk).first()
        # 這裏若是是一對多或者多對多的自定製的話就須要本身寫update方法
        bs = BookModelSerializers(book_obj, data=request.data)
        if bs.is_valid():
            bs.save(authors=request.data['authors'])
            return Response(bs.data)
        else:
            return Response(bs.errors)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response()

單條數據的get和put以及delet

class BookDetailViewSet(APIView):    

     def get(self, request, pk):

        book_obj = Book.objects.filter(pk=pk).first()
        bs = BookModelSerializers(book_obj)
        return Response(bs.data)

    def put(self, request, pk):
        book_obj = Book.objects.filter(pk=pk)
        # 這裏若是是一對多或者多對多的自定製的話就須要本身寫update方法
        bs = BookModelSerializers(book_obj, data=request.data)
        if bs.is_valid(authors=request.data['authors']): 
      bs.save()
return Response(bs.data)
else:
return Response(bs.errors)
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response()

超連接API:Hyperlinked

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__' publish = serializers.HyperlinkedIdentityField( view_name="detail_publish", lookup_field="publish_id", lookup_url_kwarg="pk", )

上面的黃色字段就是超連接的關鍵,

view_name表示的是該路由的別名獲取到publish/(?P<pk>\d+)/

lookup_field 表示在這個序列化裏面獲取到pk的值(也就是一個字段publish_id)

lookup_url_kwarg 表示把上面獲取到的pk值放到view_name的(?P<pk>\d+)裏面

在CBV相應部分

class BookDetailViewSet(APIView):
    def get(self, request, pk):
        book_obj = Book.objects.filter(pk=pk).first()
        bs = BookModelSerializers(book_obj, context={'request': request})
        return Response(bs.data)

主要是在後面添加黃色部位

路由系統爲

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^books/$', views.BookViewSet.as_view(), name="book"),

    url(r'^books/(?P<pk>\d+)/$', views.BookDetailViewSet.as_view(), name='detail_book'),
    url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailViewSet.as_view(), name='detail_publish'),
]

試圖三部曲

mixin類編寫試圖

from rest_framework import mixins
from rest_framework import generics
from app01.utils.serializers import *
class BookViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class BookDetailViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)

使用通用的基於類的視圖

經過使用mixin類,咱們使用更少的代碼重寫了這些視圖,但咱們還能夠再進一步。REST框架提供了一組已經混合好(mixed-in)的通用視圖,咱們可使用它來簡化咱們的views.py模塊。

from rest_framework import mixins
from rest_framework import generics
from app01.utils.serializers import *
class BookViewSet(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookModelSerializers class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookModelSerializers class PublishViewSet(generics.ListCreateAPIView): queryset = Publish.objects.all() serializer_class = PublshModelSerializers class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Publish.objects.all() serializer_class = PublshModelSerializers

viewsets.ModelViewSet(最終版)

urls.py

from django.conf.urls import url
from django.contrib import admin
from  app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^books/$', views.BookViewSet.as_view({"get": "list", "post": "create"}), name="book_list"),
    url(r'^books/(?P<pk>\d+)$', views.BookViewSet.as_view({
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'destroy'
    }), name="book_detail"),
]

views.py

from rest_framework import viewsets
from app01.models import *
from app01.utils.serializers import *
class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() # BookModelSerializers和前面的同樣沒什麼變化功能相似於modelform serializer_class = BookModelSerializers

 

認證

 基於token的用戶認證以及局部認證組件

沿用上面的

token:服務端動態生成的1串用來檢驗用戶身份的字符串,能夠放在header、cokies、url參數(安全性較差)、請求體(CSRF token);

token和session相似,不一樣於 session的是token比較靈活,不只僅能夠cokies裏

url.py的代碼

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.LoginView.as_view(), name="login"),

    url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"),
    url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"),

    url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"),
    url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"),

    url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"),
    url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"),

]
url.py

app01裏面的models.py的代碼

from django.db import models


# Create your models here.
class UserInfo(models.Model):
    USER_TYPE = (
        (1, '普通用戶'),
        (2, 'VIP'),
        (3, 'SVIP')
    )

    user_type = models.IntegerField(choices=USER_TYPE, default=1)
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

    def __str__(self):
        return self.username

# 設置 one to one 1個用戶不能在不一樣設備上登陸
# 設置 Freikey 支持1個用戶 在不一樣設備上同時登陸
class UserToken(models.Model):
    user = models.OneToOneField(UserInfo, on_delete=models.CASCADE)
    token = models.CharField(max_length=64)

    def __str__(self):
        return self.token


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    pub_date = models.DateField()
    publish = models.ForeignKey("Publish")
    authors = models.ManyToManyField("Author")

    def __str__(self):
        return self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    def __str__(self):
        return self.name
model.py

CBV視圖裏面的代碼

from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.views import APIView
from app01 import models
from django.http import JsonResponse
from app01.utils.auth import CustomAuthentication
from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers


# Create your views here.
def md5(user):
    import hashlib
    import time
    # 當前時間,至關於生成一個隨機的字符串
    ctime = str(time.time())
    m = hashlib.md5(bytes(user, encoding='utf-8'))
    m.update(bytes(ctime, encoding='utf-8'))
    return m.hexdigest()


class LoginView(APIView):
    """60s內任何人都只能夠登陸3次"""
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        ret = {'code': 1000, 'msg': None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用戶名或密碼錯誤'
            # 爲用戶建立token
            token = md5(user)
            # 存在就更新,不存在就建立
            models.UserToken.objects.update_or_create(user=obj, defaults={'token': token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'
        return JsonResponse(ret)
    
    
class StartAuthentication():
    """
    局部使用認證功能的狀況下,優先繼承該類
    """
    authentication_classes = [CustomAuthentication]


class AuthorModelView(StartAuthentication,viewsets.ModelViewSet):
    """無論有沒有登錄,均可以查看且60內只能看三次"""
    # 若是不使用繼承類來實現局部認證就可使用下面狀況
    # authentication_classes = [CustomAuthentication]
    queryset = models.Author.objects.all()
    serializer_class = AuthorModelSerializers
Views.py

app01/utils/auth.py 裏面的代碼

from rest_framework import authentication,exceptions
from app01.models import UserToken


class CustomAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed('驗證失敗')
        return (token_obj.user, token_obj.token)

    def authenticate_header(self, request):
        pass
auth.py

上面的局部認證只要在相應的類裏面繼承(StartAuthentication)就好了

全局視圖認證組件

只要在settings.py配置

REST_FRAMEWORK={

    "DEFAULT_AUTHENTICATION_CLASSES":["resdemo.service.auth.CustomAuthentication"]
}

上面黃色部位就是你的token認證組件的位置(也就是上面的author.py裏面的CustomAuthentication類

若是要某一個不須要認證就在其中添加

authentication_classes = []    #裏面爲空,表明不須要認證,這個就是優先執行本身的,不執行全局的,由於本身的爲空因此就是不要認證。

如上所示咱們配置了全局模式當咱們有一個不須要認證的時候就直接在這裏面加上   authentication_classes = []

使用RestAPI認證功能小結

一、建立2張表userinfo 和usertoken表

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

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

四、局部應用

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

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

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

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

本身寫認證方法總結:

一、建立認證類

  • 繼承BaseAuthentication    --->>1.重寫authenticate方法;2.authenticate_header方法直接寫pass就能夠(這個方法必須寫)                                                                                                           繼承的類在 from rest_framework import authentication中authentication.BaseAuthentication

二、authenticate()方法返回值(三種)

  • None ----->>>當前認證無論,等下一個認證來執行
  • raise exceptions.AuthenticationFailed('用戶認證失敗')       # from rest_framework import exceptions
  •  有返回值元祖形式:(元素1,元素2)      #元素1複製給request.user;  元素2複製給request.auth

三、須要建立導入的類

from rest_framework import authentication,exceptions
from app01.models import UserToken

權限組件

 添加權限

一、在app01/utils/permission.py的文件中的代碼

#!/usr/bin/evn python
#-*-coding:utf-8-*-
from rest_framework import permissions
class SVIPPermission(permissions.BasePermission):
    message = "不是SVIP不給查看"  # 當沒有權限的時候提示信息

    def has_permission(self, request, view):
        if request.user.user_type != 3:
            return False
        return True


class MyPermission(permissions.BasePermission):
    message = "普通用戶不給查看"

    def has_permission(self, request, view):
        if request.user.user_type == 1:
            return False
        return True
permission

二、在setting裏面設置全局權限

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.auth.TokenAuth"],
    "DEFAULT_PERMISSION_CLASSES": ["app01.utils.permission.SVIPPermission"],
}

若是某一個視圖不想要權限的話就能夠在該視圖中加 permission_class = []

或者不執行全局的權限只執行本身想要的權限

三、views.py 添加權限

from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.views import APIView
from app01 import models
from django.http import JsonResponse
from app01.utils.auth import CustomAuthentication
from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers
from app01.utils.permission import MyPermission


# Create your views here.
def md5(user):
    import hashlib
    import time
    # 當前時間,至關於生成一個隨機的字符串
    ctime = str(time.time())
    m = hashlib.md5(bytes(user, encoding='utf-8'))
    m.update(bytes(ctime, encoding='utf-8'))
    return m.hexdigest()


class LoginView(APIView):
    """60s內任何人都只能夠登陸3次"""
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        ret = {'code': 1000, 'msg': None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用戶名或密碼錯誤'
            # 爲用戶建立token
            token = md5(user)
            # 存在就更新,不存在就建立
            models.UserToken.objects.update_or_create(user=obj, defaults={'token': token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'
        return JsonResponse(ret)


class StartAuthentication():
    """
    局部使用認證功能的狀況下,優先繼承該類
    """
    authentication_classes = [CustomAuthentication]


class AuthorModelView(viewsets.ModelViewSet):
    """無論有沒有登錄,均可以查看且"""
    # 不執行認證和權限認證
    authentication_classes = []
    permission_classes = []
    queryset = models.Author.objects.all()
    serializer_class = AuthorModelSerializers


class BookModelView(viewsets.ModelViewSet):
    """登錄後SVIP用戶能夠查看"""
    # 執行全局的權限和認證,全局的權限是隻能SVIP用戶能夠訪問
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializers


class PublisherModelView(viewsets.ModelViewSet):
    """登錄後普通用戶不能夠看"""
    # 不執行全局的權限,只執行MyPermission這個權限
    permission_classes = [MyPermission]
    queryset = models.Publish.objects.all()
    serializer_class = PublishModelSerializers
views.py

四、url.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.LoginView.as_view(), name="login"),

    url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"),
    url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"),

    url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"),
    url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"),

    url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"),
    url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"),

]
url.py

五、app01/utils/auth.py的代碼上面有不須要改變的,這裏就不寫了

總結:

(1)使用

  • 本身寫的權限類:1.必須繼承BasePermission類;  2.必須實現:has_permission方法
  • 這個繼承的類在  from rest_framework import permission中permission.BaseAuthentication

(2)返回值

  • True   有權訪問
  • False  無權訪問

(3)局部

  • permission_classes = [MyPremission,] 

 (4)全局

REST_FRAMEWORK = {
   #權限
    "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPremission'],
}

節流

throttle(訪問頻率)組件

在app01.utils.throttle.py裏面的代碼

from rest_framework import throttling
import time

VISIT_RECORD = {}


class VisitThrottle(throttling.BaseThrottle):
    """60s內只能訪問三次"""

    def __init__(self):
        self.history = None  # 初始化訪問記錄

    def allow_request(self, request, view):
        remote_addr = self.get_ident(request)
        ctime = time.time()
        # 若是當前IP不在訪問記錄裏面,就添加到記錄
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime, ]  # 鍵值對的形式保存
            return True  # True表示能夠訪問
        # 獲取當前ip的歷史訪問記錄
        history = VISIT_RECORD.get(remote_addr)
        self.history = history
        # 若是有歷史訪問記錄,而且最先一次的訪問記錄離當前時間超過60s,就刪除最先的那個訪問記錄,
        # 只要爲True,就一直循環刪除最先的一次訪問記錄
        # 結合下面的3次能夠知道當咱們事件
        while history and history[-1] < ctime - 60:  # 最先的訪問一次加60s小於當前時間
            history.pop()
        # 若是訪問記錄不超過三次,就把當前的訪問記錄插到第一個位置(pop刪除最後一個)
        if len(history) < 3:
            history.insert(0, ctime)
            return True
        else:
            return False

    def wait(self):
        '''還須要等多久才能訪問'''
        ctime = time.time()
        return 60 - (ctime - self.history[-1])
thorttle.py

 在views.py的代碼

from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.views import APIView
from app01 import models
from django.http import JsonResponse
from app01.utils.auth import CustomAuthentication
from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers
from app01.utils.permission import MyPermission


# Create your views here.
def md5(user):
    import hashlib
    import time
    # 當前時間,至關於生成一個隨機的字符串
    ctime = str(time.time())
    m = hashlib.md5(bytes(user, encoding='utf-8'))
    m.update(bytes(ctime, encoding='utf-8'))
    return m.hexdigest()


class LoginView(APIView):
    """60s內任何人都只能夠登陸3次"""
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        ret = {'code': 1000, 'msg': None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用戶名或密碼錯誤'
            # 爲用戶建立token
            token = md5(user)
            # 存在就更新,不存在就建立
            models.UserToken.objects.update_or_create(user=obj, defaults={'token': token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'
        return JsonResponse(ret)


class StartAuthentication():
    """
    局部使用認證功能的狀況下,優先繼承該類
    """
    authentication_classes = [CustomAuthentication]


class AuthorModelView(viewsets.ModelViewSet):
    """無論有沒有登錄,均可以查看且60內只能看三次"""
    # 不執行認證和權限認證
    authentication_classes = []
    permission_classes = []
    queryset = models.Author.objects.all()
    serializer_class = AuthorModelSerializers


class BookModelView(viewsets.ModelViewSet):
    """登錄後SVIP用戶能夠查看不限次數"""
    # 執行全局的權限和認證,全局的權限是隻能SVIP用戶能夠訪問
    throttle_classes = []
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializers


class PublisherModelView(viewsets.ModelViewSet):
    """登錄後普通用戶不能夠看且60內只能看三次"""
    # 不執行全局的權限,只執行MyPermission這個權限
    permission_classes = [MyPermission]
    queryset = models.Publish.objects.all()
    serializer_class = PublishModelSerializers
Views.py

settings中全局配置節流

REST_FRAMEWORK = {
    #節流
    "DEFAULT_THROTTLE_CLASSES":['app01.utils.throttle.VisitThrottle'],
}

內置的節流類

(1)throttle.py

from rest_framework import throttling


class VisitThrottle2(throttling.SimpleRateThrottle):
    '''匿名用戶60s只能訪問三次(根據ip)'''
    scope = 'NBA'  # 這裏面的值,本身隨便定義,settings裏面根據這個值配置Rate

    def get_cache_key(self, request, view):
        # 經過ip限制節流
        return self.get_ident(request)


class UserThrottle(throttling.SimpleRateThrottle):
    '''登陸用戶60s能夠訪問10次'''
    scope = 'NBAUser'  # 這裏面的值,本身隨便定義,settings裏面根據這個值配置Rate

    def get_cache_key(self, request, view):
        return request.user.username

(2)settings.py

#全局
REST_FRAMEWORK = {
    #節流
    "DEFAULT_THROTTLE_CLASSES":['app01.utils.throttle.UserThrottle'],   #全局配置,登陸用戶節流限制(10/m)
    "DEFAULT_THROTTLE_RATES":{
        'NBA':'3/m',         #沒登陸用戶3/m,NBA就是scope定義的值
        'NBAUser':'10/m',    #登陸用戶10/m,NBAUser就是scope定義的值
    }
}

(3) views.py

from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.views import APIView
from app01 import models
from django.http import JsonResponse
from app01.utils.auth import CustomAuthentication
from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers
from app01.utils.permission import MyPermission
from app01.utils.throttle import VisitThrottle2


# Create your views here.
def md5(user):
    import hashlib
    import time
    # 當前時間,至關於生成一個隨機的字符串
    ctime = str(time.time())
    m = hashlib.md5(bytes(user, encoding='utf-8'))
    m.update(bytes(ctime, encoding='utf-8'))
    return m.hexdigest()


class LoginView(APIView):
    """60s內任何人都只能夠登陸3次"""
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        ret = {'code': 1000, 'msg': None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = '用戶名或密碼錯誤'
            # 爲用戶建立token
            token = md5(user)
            # 存在就更新,不存在就建立
            models.UserToken.objects.update_or_create(user=obj, defaults={'token': token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '請求異常'
        return JsonResponse(ret)


class StartAuthentication():
    """
    局部使用認證功能的狀況下,優先繼承該類
    """
    authentication_classes = [CustomAuthentication]


class AuthorModelView(viewsets.ModelViewSet):
    """無論有沒有登錄,均可以查看且60內只能看三次使用的是自定製的節流"""
    # 不執行認證和權限認證
    authentication_classes = []
    permission_classes = []
    # 由於全局設置的是登陸用戶(10/m),這裏不須要登陸,因此就是用匿名用戶ip限流(3/m)
    throttle_classes = [VisitThrottle2]
    queryset = models.Author.objects.all()
    serializer_class = AuthorModelSerializers


class BookModelView(viewsets.ModelViewSet):
    """登錄後SVIP用戶能夠查看不限次數"""
    # 執行全局的權限和認證,全局的權限是隻能SVIP用戶能夠訪問
    throttle_classes = []
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializers


class PublisherModelView(viewsets.ModelViewSet):
    """登錄後普通用戶不能夠看,且SVIP用戶60s內能看10次使用的是內置的節流"""
    # 不執行全局的權限,只執行MyPermission這個權限
    permission_classes = [MyPermission]
    queryset = models.Publish.objects.all()
    serializer_class = PublishModelSerializers
views.py

(4) 裏面的認證和權限和上面的同樣

說明:

  • API.utils.throttle.UserThrottle   這個是全局配置(根據ip限制,10/m)
  • DEFAULT_THROTTLE_RATES      --->>>設置訪問頻率的
  • throttle_classes = [VisitThrottle,]     --->>>局部配置(不實用settings裏面默認的全局配置,啓用如今的配置)

全部的認證、權限、節流的代碼

from django.db import models


# Create your models here.
class UserInfo(models.Model):
    USER_TYPE = (
        (1, '普通用戶'),
        (2, 'VIP'),
        (3, 'SVIP')
    )

    user_type = models.IntegerField(choices=USER_TYPE, default=1)
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

    def __str__(self):
        return self.username

# 設置 one to one 1個用戶不能在不一樣設備上登陸
# 設置 Freikey 支持1個用戶 在不一樣設備上同時登陸
class UserToken(models.Model):
    user = models.OneToOneField(UserInfo, on_delete=models.CASCADE)
    token = models.CharField(max_length=64)

    def __str__(self):
        return self.token


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    pub_date = models.DateField()
    publish = models.ForeignKey("Publish")
    authors = models.ManyToManyField("Author")

    def __str__(self):
        return self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    def __str__(self):
        return self.name
model.py
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.auth.CustomAuthentication"],
    "DEFAULT_PERMISSION_CLASSES": ["app01.utils.permission.SVIPPermission"],
    "DEFAULT_THROTTLE_CLASSES": ['app01.utils.throttle.UserThrottle'],  # 全局配置,登陸用戶節流限制(10/m)
    "DEFAULT_THROTTLE_RATES": {
        'NBA': '4/m',  # 沒登陸用戶4/m(表示60s4次),NBA就是scope定義的值
        'NBAUser': '10/m',  # 登陸用戶10/m,NBAUser就是scope定義的值
    }
}
setting
#!/usr/bin/evn python
#-*-coding:utf-8-*-
from rest_framework import permissions
class SVIPPermission(permissions.BasePermission):
    message = "不是SVIP不給查看"  # 當沒有權限的時候提示信息

    def has_permission(self, request, view):
        if request.user.user_type != 3:
            return False
        return True


class MyPermission(permissions.BasePermission):
    message = "普通用戶不給查看"

    def has_permission(self, request, view):
        if request.user.user_type == 1:
            return False
        return True
app01/utils/permission.py
#!/usr/bin/evn python
# -*-coding:utf-8-*-
from rest_framework import authentication,exceptions
from app01.models import UserToken


class CustomAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed('驗證失敗')
        return (token_obj.user, token_obj.token)

    def authenticate_header(self, request):
        pass
app01/utils/auth.py
#!/usr/bin/evn python
#-*-coding:utf-8-*-
from app01.models import *
from rest_framework import serializers
class AuthorModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = "__all__"


class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"


class PublishModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = "__all__"
app01/utils/serializers.py
#!/usr/bin/evn python
# -*-coding:utf-8-*-
from rest_framework import throttling
import time

VISIT_RECORD = {}


class VisitThrottle(throttling.BaseThrottle):
    """60s內只能訪問三次"""

    def __init__(self):
        self.history = None  # 初始化訪問記錄

    def allow_request(self, request, view):
        remote_addr = self.get_ident(request)
        ctime = time.time()
        # 若是當前IP不在訪問記錄裏面,就添加到記錄
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime, ]  # 鍵值對的形式保存
            return True  # True表示能夠訪問
        # 獲取當前ip的歷史訪問記錄
        history = VISIT_RECORD.get(remote_addr)
        self.history = history
        # 若是有歷史訪問記錄,而且最先一次的訪問記錄離當前時間超過60s,就刪除最先的那個訪問記錄,
        # 只要爲True,就一直循環刪除最先的一次訪問記錄
        # 結合下面的3次能夠知道當咱們事件
        while history and history[-1] < ctime - 60:  # 最先的訪問一次加60s小於當前時間
            history.pop()
        # 若是訪問記錄不超過三次,就把當前的訪問記錄插到第一個位置(pop刪除最後一個)
        if len(history) < 3:
            history.insert(0, ctime)
            return True
        else:
            return False

    def wait(self):
        '''還須要等多久才能訪問'''
        ctime = time.time()
        return 60 - (ctime - self.history[-1])


class VisitThrottle2(throttling.SimpleRateThrottle):
    '''匿名用戶60s只能訪問4次(根據ip)'''
    scope = 'NBA'  # 這裏面的值,本身隨便定義,settings裏面根據這個值配置Rate

    def get_cache_key(self, request, view):
        # 經過ip限制節流
        return self.get_ident(request)


class UserThrottle(throttling.SimpleRateThrottle):
    '''登陸用戶60s能夠訪問10次'''
    scope = 'NBAUser'  # 這裏面的值,本身隨便定義,settings裏面根據這個值配置Rate

    def get_cache_key(self, request, view):
        return request.user.username
app01/utils/throttle.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.LoginView.as_view(), name="login"),

    url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"),
    url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"),

    url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"),
    url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"),

    url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"),
    url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"),

]
url.py
相關文章
相關標籤/搜索