django-rest-framework

源碼繁瑣,多說無益,耐心細讀官方文檔:數據庫

https://www.django-rest-framework.org/django

 

我的總結:json

 

REST是一種軟件架構設計風格,不是標準,也不是具體的技術實現,只是提供了一組設計原則和約束條件。api

DRF(Django RestFramework)是一套基於Django開發的、幫助咱們更好的設計符合REST規範的Web應用的一個Django App,因此,本質上,它是一個Django App。瀏覽器

安裝: (肯定Django已經安裝)服務器

   >>> pip install djangorestframework

 

 

1 APIView cookie

  首先須要瞭解django中views.View類及其相關流程,看以下關係圖(最好看源碼):session

 

  DRF APIView請求流程:架構

 

 

 

  DRF對django視圖配置流程圖(我的畫)app

 

 

2 解析器組件 (用來解析數據的請求的組件)

  Django並不能處理請求協議爲application/json編碼協議的數據

  注意: DRF解析器會封裝到View中的parsers內,在視圖函數被調用時,會傳入request,經過request.data拿到數據才進行解析 ,即解析器解析是在request對象傳入後.

  解析器組件流程圖:

 

 

//解析器的使用方式: //1,導入模塊 views.py from rest_framwork.views import APIView //2, 繼承APIView class BookView(APIView): def get(self, request): pass

    //3, url.py from django.urls import path, include, re_path from classbasedview import views urlpatterns = [ re_path('login/$', views.LoginView.as_view()), ] //4, def post(self, request): origin_data = request.data ... return HttpResponse({})  

 

  試用工具: postman---經過postman來模擬用戶請求,再也不須要使用瀏覽器來發送請求.(直接在官網下載便可)


3 序列化組件 

  序列化組件的使用:

  --get接口設計:

  • 導入序列化組件:from rest_framework import serializers
  • 定義序列化類,繼承serializers.Serializer(建議單首創建一個專用的模塊用來存放全部的序列化類):class BookSerializer(serializers.Serializer):pass
  • 定義須要返回的字段(字段類型能夠與model中的類型不一致,參數也能夠調整),字段名稱必須與model中的一致
  • 在GET接口邏輯中,獲取QuerySet
  • 開始序列化:將QuerySet做業第一個參數傳給序列化類,many默認爲False,若是返回的數據是一個列表嵌套字典的多個對象集合,須要改成many=True
  • 返回:將序列化對象的data屬性返回便可

 

  {{ 實踐代碼 }}

  --post接口設計

  • url定義:須要爲post新增url,由於根據規範,url定位資源,http請求方式定義用戶行爲
  • 定義post方法:在視圖類中定義post方法
  • 開始序列化:經過咱們上面定義的序列化類,建立一個序列化對象,傳入參數data=request.data(application/json)數據
  • 校驗數據:經過實例對象的is_valid()方法,對請求數據的合法性進行校驗
  • 保存數據:調用save()方法,將數據插入數據庫
  • 插入數據到多對多關係表:若是有多對多字段,手動插入數據到多對多關係表
  • 返回:將插入的對象返回

  {{ 實踐代碼 }}

  使數據自動插入並且更加簡單:

class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ('title', 'price', 'publish', 'authors', 'author_list', 'publish_name', 'publish_city' ) extra_kwargs = { 'publish': {'write_only': True}, 'authors': {'write_only': True} } publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name') publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') author_list = serializers.SerializerMethodField() def get_author_list(self, book_obj): # 拿到queryset開始循環 [{}, {}, {}, {}]
        authors = list() for author in book_obj.authors.all(): authors.append(author.name) return authors

  步驟以下:

    繼承ModelSerializer: 再也不繼承Serializer

    添加extra_kwargs類變量: extra_kwargs = { 'publish':{'write_only':True}}

 

4 視圖組件

  使用視圖組件進行接口邏輯化

   導入mixin

from rest_framework.mixinx import ( ListModelMix, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin ) from rest_framework.generics import GenericAPIView 

    定義序列化類

Class BookSerializer(serializers.ModelSerializer): class Meta: Book fields = () extra_kwargs = {"field_name": {"write_only": True}}

 

    導入序列化類

    from .app_serializers import BookSerializer

  定義視圖類

            class BookView(ListModelMix, CreateModelMixin, GenericAPIView): # queryset和serializer_class是固定的寫法
                queryset = Book.objects.all() serializer_class = BookSerializer def get(): return self.list() def post(): return self.create() class BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer def get(): return self.retrieve() def delete(): return self.destroy() def put(): return self.update()

 

   注意: 單條數據操做的url是這樣的:re_path(r'books/(?P<pk>\d+)/$, views.BookFilterView.as_view())

 

  使用視圖組件的view進行接口邏輯優化

    導入模塊  from rest_framework import generics

    寫試圖類

class BookView(generics.ListCreateAPIView) queryset = Book.objects.all() serializer_class = BookSerializer class BookFilterView(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer

 

  使用視圖組件的viewset進行接口邏輯優化

    導入模塊 from rest_framework.viewset import ModelViewSet

    設計url

re_path(r'books/$, views.BookView.as_view({
                    'get': 'list', 'post': 'create' })), re_path(r'books/(?P<pk>\d+)/$', views.BookView.as_view({ 'get': 'retrieve', 'delete': 'destroy', 'put': 'update' }))

 

 

 

    設計視圖類

       class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer

 

 

  • Django程序啓動,開始初始化,獲取配置信息,獲取視圖類並加載到內存中,獲取url及視圖類的對應關係
  • 開始綁定視圖類和url的對應關係,執行as_view()方法
  • as_view()方法被執行的時候傳遞了參數,爲字典形式:{ 「get」: 「retrieve」, 「delete」: 「destroy」, 「put」: 「update」 }
  • 上一步中執行as_view()方法傳遞參數的目的是爲了完成優化,將delete請求方式從新命名爲不一樣的函數
  • ViewSetMixin類重寫了as_view()方法,也就是在這個地方將幾個函數從新綁定,它並無重寫dispatch方法
  • 該方法返回視圖函數view,注意在這個函數中有一個行 self = cls(**initkwargs), cls是視圖類,執行視圖函數時self就指向視圖函數的實例對象
  • 等待客戶端請求
  • 請求到來,開始執行視圖函數,注意,調用視圖函數時的方式是view(request),而若是url帶有參數,調用方式爲view(request, xxx=id)的形式
  • 顯然,咱們有命名參數(?P\d+),因此此時的調用方式爲view(request, pk=id)
  • 視圖函數中有一行self.kwargs = kwargs,因此pk已經被視圖函數找到了
  • 視圖函數返回self.dispatch(),開始執行dispatch方法,注意self是視圖類的實例化對象(每一個請求都被封裝爲一個對象)
  • dispatch開始執行get方法,注意此時的get方法會執行retrieve,覺得已經被重定向了
  • 開始執行retrieve,有一行instance = self.get_object(), 該方法在GenericAPIView中
  • 相當重要的是拿到self.kwargs中的pk關鍵字,而後從queryset中拿到想要的數據
  • 返回結果

 

 

5 認證組件

  cookie和session兩種方式能夠保存用戶信息,這兩種方式不一樣的是cookie保存在客戶端瀏覽器中,而session保存在服務器中,他們各有優缺點,配合起來使用,可將重要的敏感的信息存儲在session中,而在cookie中能夠存儲不太敏感的數據。

  token稱之爲令牌。cookie、session和token都有其應用場景,沒有誰好誰壞,不過開發數據接口類的Web應用,目前用token仍是比較多的。

  token認證步驟:

    用戶登陸,服務器端獲取密碼,查詢用戶表,若是存在該用戶且第一次登陸(或者token過時), 生成token,不然返回錯誤信息

    若是用戶不是第一次登陸,且token未過時,更新token值

  建立倆個model,(token能夠存儲在user表中,建議存儲在user表中):

    

from django.db import models # Create your models here.

class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) user_type_entry = ( (1, 'Delux'), (2, 'SVIP'), (3, "VVIP") ) user_type = models.IntegerField(choices=user_type_entry) address = models.CharField(max_length=32) def __str__(self): return self.username class UserToken(models.Model): user = models.OneToOneField("User", on_delete=models.CASCADE) token = models.CharField(max_length=128)

  由於涉及登陸認證,因此寫post方法接口,登陸都是post請求:

  

from django.http import JsonResponse from rest_framework.views import APIView from .models import User, Book, UserToken from .utils import get_token class UserView(APIView): def post(self, request): response = dict() try: username = request.data['username'] password = request.data['password'] user_instance = User.objects.filter( user_name=username, password=password ).first() if user_instance: access_token = get_token.generater_token() UserToken.objects.update_or_create(user=user_instance, defaults={ "token": access_token }) response["status_code"] = 200 response["status_message"] = "登陸成功" response["access_token"] = access_token response["user_role"] = user_instance.get_user_type_display() else: response["status_code"] = 201 response["status_message"] = "登陸失敗,用戶名或密碼錯誤"
        except Exception as e: response["status_code"] = 202 response["status_message"] = str(e) return JsonResponse(response)

  經過獲取隨機字符串的方法用來生成token值:

# -*- coding: utf-8 -*-
import uuid def generater_token(): random_str = ''.join(str(uuid.uuid4()).split('-')) return random_str

  DRF認證組件的使用:

    新建一個認證類,包含以後的認證邏輯:

class UserAuth(object): def authenticate_header(self, request): pass

    def authenticate(self, request): user_post_token = request.query_params.get('token') token_object = UserToken.objects.filter(token=user_post_token).first() if token_object: return token_object.user.username, token_object.token else: raise APIException("認證失敗")

  實現方式看上去很是簡單,到token表裏面查看token是否存在,而後根據這個信息,返回對應信息便可,而後,在須要認證經過才能訪問的數據接口裏面註冊認證類便可: 

class BookView(ModelViewSet): authentication_classes = [UserAuth, UserAuth2] queryset = Book.objects.all() serializer_class =  BookSerializer

  多個認證類實現:

    注意:若須要返回數據,請在最後一個認證類中返回,由於在前面返回,self.authentication()方法中會對返回值進行判斷,若不爲空,認證的過程就會終止.  多個認證類實現方式以下:

class UserAuth2(object): def authenticate(self, request): raise APIException("認證失敗") class UserAuth(object): def authenticate_header(self, request): pass

    def authenticate(self, request): user_post_token = request.query_params.get('token') token_object = UserToken.objects.filter(token=user_post_token).first() if token_object: return token_object.user.username, token_object.token else: raise APIException("認證失敗") class BookView(ModelViewSet): authentication_classes = [UserAuth, UserAuth2]

  簡化authenticate_header方法,以下:(繼承BaseAuthentication類便可)

from rest_framework.authentication import BaseAuthentication class UserAuth2(BaseAuthentication): def authenticate(self, request): raise APIException("認證失敗") class UserAuth(BaseAuthentication): def authenticate(self, request): user_post_token = request.query_params.get('token') token_object = UserToken.objects.filter(token=user_post_token).first() if token_object: return token_object.user.user_name, token_object.token else: raise APIException("認證失敗")

 

  全局認證: 

    實現全部的數據接口都須要認證:  

authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES

  若是認證類本身沒有authentication_classes,就會到settings中去找,經過這個機制,咱們能夠將認證類寫入到settings文件中便可實現全局認證:

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'authenticator.utils.authentication.UserAuth', 'authenticator.utils.authentication.UserAuth2', ), }

 

6 權限組件 

  定義權限類: 

class UserPerms(): message = "您沒有權限訪問該數據"
    def has_permission(self, request, view): if request.user.user_type > 2: return True else: return False

  一樣的邏輯,一樣的方式,只是執行權限的方法名與執行認證的方法名不同而已,名爲has_permission,而且須要將當前的視圖類傳遞給該方法。

  視圖類中加入permission_classes變量:

class BookView(ModelViewSet): authentication_classes = [UserAuth] permission_classes = [UserPerms2] queryset = Book.objects.all() serializer_class =  BookSerializer

 

7 頻率組件


8 url控制器組件
9 分頁器組件
10 響應器組件

相關文章
相關標籤/搜索