DRF-Django Rest frameWork脈絡筆記

1. Web應用模式

在開發Web應用中,有兩種應用模式:

  1. 前後端不分離
    在這裏插入圖片描述
  2. 前後端分離
    在這裏插入圖片描述

2. api接口

爲了在團隊內部形成共識、防止個人習慣差異引起的混亂,我們需要找到一種大家都覺得很好的接口實現規範,而且這種規範能夠讓後端寫的接口,用途一目瞭然,減少雙方之間的合作成本。

目前市面上大部分公司開發人員使用的接口服務架構主要有:restful、rpc。

rpc: 翻譯成中文:遠程過程調用[遠程服務調用].

http://www.lufei.com/api

post請求

action=get_all_student&params=301&sex=1

接口多了,對應函數名和參數就多了,前端在請求api接口時,就會比較難找.容易出現重複的接口

restful: 翻譯成中文: 資源狀態轉換.

把後端所有的數據/文件都看成資源.

那麼接口請求數據,本質上來說就是對資源的操作了.

web項目中操作資源,無非就是增刪查改.所以要求在地址欄中聲明要操作的資源是什麼,然後通過http請求動詞來說明對資源進行哪一種操作.

POST http://www.lufei.com/api/students/ 添加學生數據

GET http://www.lufei.com/api/students/ 獲取所有學生

DELETE http://www.lufei.com/api/students/ 刪除1個學生

3. RESTful API規範

在這裏插入圖片描述REST全稱是Representational State Transfer,中文意思是表述(編者注:通常譯爲表徵)性狀態轉移。 它首次出現在2000年Roy Fielding的博士論文中。

RESTful是一種定義Web API接口的設計風格,尤其適用於前後端分離的應用模式中。

這種風格的理念認爲後端開發任務就是提供數據的,對外提供的是數據資源的訪問接口,所以在定義接口時,客戶端訪問的URL路徑就表示這種要操作的數據資源。

而對於數據資源分別使用POST、DELETE、GET、UPDATE等請求動作來表達對數據的增刪查改。

請求方法 請求地址 後端操作
GET /students 獲取所有學生
POST /students 增加學生
GET /students/ 獲取編號爲pk的學生
PUT /students/ 修改編號爲pk的學生
DELETE /students/ 刪除編號爲pk的學生

事實上,我們可以使用任何一個框架都可以實現符合restful規範的API接口。

參考文檔:http://www.runoob.com/w3cnote/restful-architecture.html

4. 序列化

api接口開發,最核心最常見的一個過程就是序列化,所謂序列化就是把數據轉換格式,序列化可以分兩個階段:

序列化: 把我們識別的數據轉換成指定的格式提供給別人。

例如:我們在django中獲取到的數據默認是模型對象,但是模型對象數據無法直接提供給前端或別的平臺使用,所以我們需要把數據進行序列化,變成字符串或者json數據,提供給別人。

反序列化:把別人提供的數據轉換/還原成我們需要的格式。

例如:前端js提供過來的json數據,對於python而言就是字符串,我們需要進行反序列化換成模型類對象,這樣我們才能把數據保存到數據庫中。

5. Django Rest_Framework

核心思想: 縮減編寫api接口的代碼

Django REST framework是一個建立在Django基礎之上的Web 應用開發框架,可以快速的開發REST API接口應用。在REST framework中,提供了序列化器Serialzier的定義,可以幫助我們簡化序列化與反序列化的過程,不僅如此,還提供豐富的類視圖、擴展類、視圖集來簡化視圖的編寫工作。REST framework還提供了認證、權限、限流、過濾、分頁、接口文檔等功能支持。REST framework提供了一個API 的Web可視化界面來方便查看測試接口。

在這裏插入圖片描述
中文文檔:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework

github: https://github.com/encode/django-rest-framework/tree/master

特點

  • 提供了定義序列化器Serializer的方法,可以快速根據 Django ORM 或者其它庫自動序列化/反序列化;
  • 提供了豐富的類視圖、Mixin擴展類,簡化視圖的編寫;
  • 豐富的定製層級:函數視圖、類視圖、視圖集合到自動生成 API,滿足各種需要;
  • 多種身份認證和權限認證方式的支持;[jwt]
  • 內置了限流系統;
  • 直觀的 API web 界面;
  • 可擴展性,插件豐富

6. 環境安裝與配置

DRF需要以下依賴:

  • Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)
  • Django (1.10, 1.11, 2.0)

DRF是以Django擴展應用的方式提供的,所以我們可以直接利用已有的Django環境而無需從新創建。(若沒有Django環境,需要先創建環境安裝Django)

6.1 安裝DRF

前提是已經安裝了django,建議安裝在虛擬環境

# mkvirtualenv drfdemo -p python3
# pip install django

pip install djangorestframework
pip install pymysql

linux 複製 shift+insert

6.1.1 創建django項目

cd ~/Desktop
django-admin startproject drfdemo

在這裏插入圖片描述
使用pycharm打開項目,設置虛擬環境的解析器,並修改manage.py中的後綴參數。

6.2 添加rest_framework應用

settings.pyINSTALLED_APPS中添加’rest_framework’。

INSTALLED_APPS = [
    ...
    'rest_framework',
]

接下來就可以使用DRF提供的功能進行api接口開發了。在項目中如果使用rest_framework框架實現API接口,主要有以下三個步驟:

  • 將請求的數據(如JSON格式)轉換爲模型類對象
  • 操作數據庫
  • 將模型類對象轉換爲響應的數據(如JSON格式)

接下來,我們快速體驗下四天後我們學習完成drf以後的開發代碼。接下來代碼不需要理解,看步驟。

6.3 體驗drf完全簡寫代碼的過程

6.3.1. 創建模型操作類

class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名")
    sex = models.BooleanField(default=1,verbose_name="性別")
    age = models.IntegerField(verbose_name="年齡")
    class_null = models.CharField(max_length=5,verbose_name="班級編號")
    description = models.TextField(max_length=1000,verbose_name="個性簽名")

    class Meta:
        db_table="tb_student"
        verbose_name = "學生"
        verbose_name_plural = verbose_name

爲了方便測試,所以我們可以先創建一個數據庫。

create database students charset=utf8;

在這裏插入圖片描述

6.3.1.1 執行數據遷移

把students子應用添加到INSTALL_APPS中
在這裏插入圖片描述

初始化數據庫連接

安裝pymysql
pip install pymysql

主引用中__init__.py設置使用pymysql作爲數據庫驅動

import pymysql

pymysql.install_as_MySQLdb()

settings.py配置文件中設置mysql的賬號密碼

DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # },
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "students",
        "HOST": "127.0.0.1",
        "PORT": 3306,
        "USER": "root",
        "PASSWORD":"123",
    },
}

終端下,執行數據遷移。

python manage.py makemigrations
python manage.py migrate

錯誤列表

# 執行數據遷移 python manage.py makemigrations 報錯如下:

在這裏插入圖片描述

解決方案:

註釋掉 backends/mysql/base.py中的3536行代碼。

在這裏插入圖片描述

# 執行數據遷移發生以下錯誤:

在這裏插入圖片描述

解決方法:

backends/mysql/operations.py146行裏面新增一個行代碼:
在這裏插入圖片描述

6.3.2. 創建序列化器

例如,在django項目中創建學生子應用。

python manage.py startapp students

在syudents應用目錄中新建serializers.py用於保存該應用的序列化器。

創建一個StudentModelSerializer用於序列化與反序列化。

# 創建序列化器類,回頭會在試圖中被調用
class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
  • model 指明該序列化器處理的數據字段從模型類BookInfo參考生成
  • fields 指明該序列化器包含模型類中的哪些字段,'all’指明包含所有字段

6.3.3. 編寫視圖

在students應用的views.py中創建視圖StudentViewSet,這是一個視圖集合。

from rest_framework.viewsets import ModelViewSet
from .models import Student
from .serializers import StudentModelSerializer
# Create your views here.
class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
  • queryset 指明該視圖集在查詢數據時使用的查詢集
  • serializer_class 指明該視圖在進行序列化或反序列化時使用的序列化器

6.3.4. 定義路由

在students應用的urls.py中定義路由信息。

from . import views
from rest_framework.routers import DefaultRouter

# 路由列表
urlpatterns = []

router = DefaultRouter()  # 可以處理視圖的路由器
router.register('students', views.StudentViewSet)  # 向路由器中註冊視圖集

urlpatterns += router.urls  # 將路由器中的所以路由信息追到到django的路由列表中

最後把students子應用中的路由文件加載到總路由文件中.

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("stu/",include("students.urls")),
]

6.3.5. 運行測試

運行當前程序(與運行Django一樣)

python manage.py runserver

在瀏覽器中輸入網址127.0.0.1:8000,可以看到DRF提供的API Web瀏覽頁面:
在這裏插入圖片描述

1)點擊鏈接127.0.0.1:8000/stu/students 可以訪問獲取所有數據的接口,呈現如下頁面:
在這裏插入圖片描述

2)在頁面底下表單部分填寫學生信息,可以訪問添加新學生的接口,保存學生信息:
在這裏插入圖片描述

點擊POST後,返回如下頁面信息:
在這裏插入圖片描述

3)在瀏覽器中輸入網址127.0.0.1:8000/stu/students/5/,可以訪問獲取單一學生信息的接口(id爲5的學生),呈現如下頁面:
在這裏插入圖片描述

4)在頁面底部表單中填寫學生信息,可以訪問修改學生的接口
在這裏插入圖片描述

點擊PUT,返回如下頁面信息:
在這裏插入圖片描述

5)點擊DELETE按鈕,可以訪問刪除學生的接口
在這裏插入圖片描述

返回,如下頁面:
在這裏插入圖片描述

7. 序列化器-Serializer

作用:

1. 序列化,序列化器會把模型對象轉換成字典,經過response以後變成json字符串
2. 反序列化,把客戶端發送過來的數據,經過request以後變成字典,序列化器可以把字典轉成模型
3. 反序列化,完成數據校驗功能

7.1 定義序列化器

Django REST framework中的Serializer使用類來定義,須繼承自rest_framework.serializers.Serializer。

接下來,爲了方便演示序列化器的使用,我們先創建一個新的子應用sers

python manage.py startapp sers

我們已有了一個數據庫模型類students/Student

from django.db import models

# Create your models here.
class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名",help_text="提示文本:賬號不能爲空!")
    sex = models.BooleanField(default=True,verbose_name="性別")
    age = models.IntegerField(verbose_name="年齡")
    class_null = models.CharField(max_length=5,verbose_name="班級編號")
    description = models.TextField(verbose_name="個性簽名")

    class Meta:
        db_table="tb_student"
        verbose_name = "學生"
        verbose_name_plural = verbose_name

我們想爲這個模型類提供一個序列化器,可以定義如下:

from rest_framework import serializers

# 聲明序列化器,所有的序列化器都要直接或者間接繼承於 Serializer
# 其中,ModelSerializer是Serializer的子類,ModelSerializer在Serializer的基礎上進行了代碼簡化
class StudentSerializer(serializers.Serializer):
    """學生信息序列化器"""
    # 1. 需要進行數據轉換的字段
    id = serializers.IntegerField()
    name = serializers.CharField()
    age = serializers.IntegerField()
    sex = serializers.BooleanField()
    description = serializers.CharField()

    # 2. 如果序列化器集成的是ModelSerializer,則需要聲明調用的模型信息

    # 3. 驗證代碼

    # 4. 編寫添加和更新模型的代碼

**注意:serializer不是隻能爲數據庫模型類定義,也可以爲非數據庫模型類的數據定義。**serializer是獨立於數據庫之外的存在。

常用字段類型

字段 字段構造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正則字段,驗證正則模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressField IPAddressField(protocol=‘both’, unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位數 decimal_palces: 小數點位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices與Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)

選項參數:

參數名稱 作用
max_length 最大長度
min_lenght 最小長度
allow_blank 是否允許爲空
trim_whitespace 是否截斷空白字符
max_value 最小值
min_value 最大值

通用參數:

參數名稱 說明
read_only 表明該字段僅用於序列化輸出,默認False
write_only 表明該字段僅用於反序列化輸入,默認False
required 表明該字段在反序列化時必須輸入,默認True
default 反序列化時使用的默認值
allow_null 表明該字段是否允許傳入None,默認False
validators 該字段使用的驗證器
error_messages 包含錯誤編號與錯誤信息的字典
label 用於HTML展示API頁面時,顯示的字段名稱
help_text 用於HTML展示API頁面時,顯示的字段幫助提示信息

7.2 創建Serializer對象

定義好Serializer類後,就可以創建Serializer對象了。

Serializer的構造方法爲:

Serializer(instance=None, data=empty, **kwarg)

說明:

1)用於序列化時,將模型類對象傳入instance參數

2)用於反序列化時,將要被反序列化的數據傳入data參數

3)除了instance和data參數外,在構造Serializer對象時,還可通過context參數額外添加數據,如

serializer = AccountSerializer(account, context={'request': request})

通過context參數附加的數據,可以通過Serializer對象的context屬性獲取。

  1. 使用序列化器的時候一定要注意,序列化器聲明瞭以後,不會自動執行,需要我們在視圖中進行調用纔可以。
  2. 序列化器無法直接接收數據,需要我們在視圖中創建序列化器對象時把使用的數據傳遞過來。
  3. 序列化器的字段聲明類似於我們前面使用過的表單系統。
  4. 開發restful api時,序列化器會幫我們把模型數據轉換成字典.
  5. drf提供的視圖會幫我們把字典轉換成json,或者把客戶端發送過來的數據轉換字典.

7.3 序列化器的使用

序列化器的使用分兩個階段:

  1. 在客戶端請求時,使用序列化器可以完成對數據的反序列化。
  2. 在服務器響應時,使用序列化器可以完成對數據的序列化。

7.3.1 序列化

7.3.1.1 基本使用

1) 先查詢出一個學生對象

from students.models import Student

student = Student.objects.get(id=3)

2) 構造序列化器對象

from .serializers import StudentSerializer

serializer = StudentSerializer(instance=student)

3)獲取序列化數據

通過data屬性可以獲取序列化後的數據

serializer.data
# {'id': 4, 'name': '小張', 'age': 18, 'sex': True, 'description': '猴賽雷'}

完整視圖代碼:

from django.views import View
from students.models import Student
from .serializers import StudentSerializer
from django.http.response import JsonResponse
class StudentView(View):
    """使用序列化器序列化轉換單個模型數據"""
    def get(self,request,pk):
        # 獲取數據
        student = Student.objects.get(pk=pk)
        # 數據轉換[序列化過程]
        serializer = StudentSerializer(instance=student)
        print(serializer.data)
        # 響應數據
        return JsonResponse(serializer.data)

4)如果要被序列化的是包含多條數據的查詢集QuerySet,可以通過添加many=True參數補充說明

    """使用序列化器序列化轉換多個模型數據"""
    def get(self,request):
        # 獲取數據
        student_list = Student.objects.all()

        # 轉換數據[序列化過程]
        # 如果轉換多個模型對象數據,則需要加上many=True
        serializer = StudentSerializer(instance=student_list,many=True)
        print( serializer.data ) # 序列化器轉換後的數據

        # 響應數據給客戶端
        # 返回的json數據,如果是列表,則需要聲明safe=False
        return JsonResponse(serializer.data,safe=False)
    
    
    # 訪問結果:
    # [OrderedDict([('id', 1), ('name', 'xiaoming'), ('age', 20), ('sex', True), ('description', '測試')]), OrderedDict([('id', 2), ('name', 'xiaohui'), ('age', 22), ('sex', True), ('description', '後面來的測試')]), OrderedDict([('id', 4), ('name', '小張'), ('age', 18), ('sex', True), ('description', '猴賽雷')])]

7.3.2 反序列化

7.3.2.1 數據驗證

使用序列化器進行反序列化時,需要對數據進行驗證後,才能獲取驗證成功的數據或保存成模型類對象。

在獲取反序列化的數據前,必須調用**is_valid()**方法進行驗證,驗證成功返回True,否則返回False。

驗證失敗,可以通過序列化器對象的errors屬性獲取錯誤信息,返回字典,包含了字段和字段的錯誤。如果是非字段錯誤,可以通過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯誤字典中的鍵名。

驗證成功,可以通過序列化器對象的validated_data屬性獲取數據。

在定義序列化器時,指明每個字段的序列化類型和選項參數,本身就是一種驗證行爲。

如我們前面定義過的BookInfoSerializer

class BookInfoSerializer(serializers.Serializer):
    """圖書數據序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名稱', max_length=20)
    bpub_date = serializers.DateField(label='發佈日期', required=False)
    bread = serializers.IntegerField(label='閱讀量', required=False)
    bcomment = serializers.IntegerField(label='評論量', required=False)
    image = serializers.ImageField(label='圖片', required=False)

通過構造序列化器對象,並將要反序列化的數據傳遞給data構造參數,進而進行驗證

from booktest.serializers import BookInfoSerializer
data = {'bpub_date': 123}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # 返回False
serializer.errors
# {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data  # {}

data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # True
serializer.errors  # {}
serializer.validated_data  #  OrderedDict([('btitle', 'python')])

is_valid()方法還可以在驗證失敗時拋出異常serializers.ValidationError,可以通過傳遞raise_exception=True參數開啓,REST framework接收到此異常,會向前端返回HTTP 400 Bad Request響應。

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

如果覺得這些還不夠,需要再補充定義驗證行爲,可以使用以下三種方法:

1) validate_字段名

<field_name>字段進行驗證,如

class BookInfoSerializer(serializers.Serializer):
    """圖書數據序列化器"""
    ...

    def validate_btitle(self, value):
        if 'django' not in value.lower():
            raise serializers.ValidationError("圖書不是關於Django的")
        return value

測試

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # False   
serializer.errors
#  {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}
2) validate

在序列化器中需要同時對多個字段進行比較驗證時,可以定義validate方法來驗證,如

class BookInfoSerializer(serializers.Serializer):
    """圖書數據序列化器"""
    ...

    def validate(self, attrs):
        bread = attrs['bread']
        bcomment = attrs['bcomment']
        if bread < bcomment:
            raise serializers.ValidationError('閱讀量小於評論量')
        return attrs

測試

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20}
s = BookInfoSerializer(data=data)
s.is_valid()  # False
s.errors
#  {'non_field_errors': [ErrorDetail(string='閱讀量小於評論量', code='invalid')]}
3) validators

在字段中添加validators選項參數,也可以補充驗證行爲,如

def about_django(value):
    if 'django' not in value.lower():
        raise serializers.ValidationError("圖書不是關於Django的")

class BookInfoSerializer(serializers.Serializer):
    """圖書數據序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名稱', max_length=20, validators=[about_django])
    bpub_date = serializers.DateField(label='發佈日期', required=False)
    bread = serializers.IntegerField(label='閱讀量', required=False)
    bcomment = serializers.IntegerField(label='評論量', required=False)
    image = serializers.ImageField(label='圖片', required=False)

測試:

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # False   
serializer.errors
#  {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}

7.3.2.2 反序列化-保存數據

序列化用data=

反序列化用instance=

前面的驗證數據成功後,我們可以使用序列化器來完成數據反序列化的過程.這個過程可以把數據轉成模型類對象.

可以通過實現create()和update()兩個方法來實現。

class BookInfoSerializer(serializers.Serializer):
    """圖書數據序列化器"""
    ...

    def create(self, validated_data):
        """新建"""
        return BookInfo(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance爲要更新的對象實例"""
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        return instance

如果需要在返回數據對象的時候,也將數據保存到數據庫中,則可以進行如下修改

class BookInfoSerializer(serializers.Serializer):
    """圖書數據序列化器"""
    ...

    def create(self, validated_data):
        """新建"""
        return BookInfo.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance爲要更新的對象實例"""
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        instance.save()
        return instance

實現了上述兩個方法後,在反序列化數據的時候,就可以通過save()方法返回一個數據對象實例了

book = serializer.save()

如果創建序列化器對象的時候,沒有傳遞instance實例,則調用save()方法的時候,create()被調用,相反,如果傳遞了instance實例,則調用save()方法的時候,update()被調用。

from db.serializers import BookInfoSerializer
data = {'btitle': '封神演義'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: 封神演義>

from db.models import BookInfo
book = BookInfo.objects.get(id=2)
data = {'btitle': '倚天劍'}
serializer = BookInfoSerializer(book, data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: 倚天劍>
book.btitle  # '倚天劍'

7.3.2.3 附加說明

1) 在對序列化器進行save()保存時,可以額外傳遞數據,這些數據可以在create()和update()中的validated_data參數獲取到

# request.user 是django中記錄當前登錄用戶的模型對象
serializer.save(owner=request.user)

2)默認序列化器必須傳遞所有required的字段,否則會拋出驗證異常。但是我們可以使用partial參數來允許部分字段更新

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

7.3.3 模型類序列化器

如果我們想要使用序列化器對應的是Django的模型類,DRF爲我們提供了ModelSerializer模型類序列化器來幫助我們快速創建一個Serializer類。

ModelSerializer與常規的Serializer相同,但提供了:

  • 基於模型類自動生成一系列字段
  • 基於模型類自動爲Serializer生成validators,比如unique_together
  • 包含默認的create()和update()的實現

7.3.3.1 定義

比如我們創建一個BookInfoSerializer

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        fields = '__all__'
  • model 指明參照哪個模型類
  • fields 指明爲模型類的哪些字段生成

我們可以在python manage.py shell中查看自動生成的BookInfoSerializer的具體實現

>>> from booktest.serializers import BookInfoSerializer
>>> serializer = BookInfoSerializer()
>>> serializer
BookInfoSerializer():
    id = IntegerField(label='ID', read_only=True)
    btitle = CharField(label='名稱', max_length=20)
    bpub_date = DateField(allow_null=True, label='發佈日期', required=False)
    bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=-2147483648, required=False)
    bcomment = IntegerField(label='評論量', max_value=2147483647, min_value=-2147483648, required=False)
    image = ImageField(allow_null=True, label='圖片', max_length=100, required=False)

7.3.3.2 指定字段

  1. 使用fields來明確字段,__all__表名包含所有字段,也可以寫明具體哪些字段,如
class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date')
  1. 使用exclude可以明確排除掉哪些字段
class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        exclude = ('image',)
  1. 顯示指明字段,如:
class HeroInfoSerializer(serializers.ModelSerializer):
    hbook = BookInfoSerializer()

    class Meta:
        model = HeroInfo
        fields = ('id', 'hname', 'hgender', 'hcomment', 'hbook')
  1. 指明只讀字段

可以通過read_only_fields指明只讀字段,即僅用於序列化輸出的字段

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date''bread', 'bcomment')
        read_only_fields = ('id', 'bread', 'bcomment')

7.3.3.3 添加額外參數

我們可以使用extra_kwargs參數爲ModelSerializer添加或修改原有的選項參數

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
        extra_kwargs = {
            'bread': {'min_value': 0, 'required': True},
            'bcomment': {'min_value': 0, 'required': True},
        }

# BookInfoSerializer():
#    id = IntegerField(label='ID', read_only=True)
#    btitle = CharField(label='名稱', max_length=20)
#    bpub_date = DateField(allow_null=True, label='發佈日期', required=False)
#    bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=0, required=True)
#    bcomment = IntegerField(label='評論量', max_value=2147483647, min_value=0, required=True)

什麼時候聲明的序列化器需要繼承序列化器基類Serializer,什麼時候繼承模型序列化器類ModelSerializer?

繼承序列化器類Serializer
	字段聲明
	驗證
	添加/保存數據功能
繼承模型序列化器類ModelSerializer
	字段聲明[可選,看需要]
	Meta聲明
	驗證
	添加/保存數據功能[可選]

看錶字段大小,看使用哪個更加節省代碼了。

8. 視圖相關

drf除了在數據序列化部分簡寫代碼以外,還在視圖中提供了簡寫操作。所以在django原有的django.views.View類基礎上,drf封裝了多個子類出來提供給我們使用。

Django REST framwork 提供的視圖的主要作用:

  • 控制序列化器的執行(檢驗、保存、轉換數據)
  • 控制數據庫查詢的執行
  • 調用請求類和響應類[這兩個類也是由drf幫我們再次擴展了一些功能類。]

爲了方便我們學習,所以先創建一個子應用req

python manage.py startapp req

1. 請求與響應

1.1 Request

REST framework 傳入視圖的request對象不再是Django默認的HttpRequest對象,而是REST framework提供的擴展了HttpRequest類的Request類的對象。

REST framework 提供了Parser解析器,在接收到請求後會自動根據Content-Type指明的請求數據類型(如JSON、表單等)將請求數據進行parse解析,解析爲類字典[QueryDict]對象保存到Request對象中。

Request對象的數據是自動根據前端發送數據的格式進行解析之後的結果。

無論前端發送的哪種格式的數據,我們都可以以統一的方式讀取數據。

1.1.1 常用屬性

1).data

request.data 返回解析之後的請求體數據。類似於Django中標準的request.POSTrequest.FILES屬性,但提供如下特性:

  • 包含了解析之後的文件和非文件數據
  • 包含了對POST、PUT、PATCH請求方式解析後的數據
  • 利用了REST framework的parsers解析器,不僅支持表單類型數據,也支持JSON數據
2).query_params

request.query_params與Django標準的request.GET相同,只是更換了更正確的名稱而已。

1.2 Response

rest_framework.response.Response

REST framework提供了一個響應類Response,使用該類構造響應對象時,響應的具體數據內容會被轉換(render渲染)成符合前端需求的類型。

REST framework提供了Renderer 渲染器,用來根據請求頭中的Accept(接收數據類型聲明)來自動轉換響應數據到對應格式。如果前端請求中未進行Accept聲明,則會採用默認方式處理響應數據,我們可以通過配置來修改默認響應格式。

可以在rest_framework.settings查找所有的drf默認配置項

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 默認響應渲染類
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 瀏覽API渲染器
    )
}

1.2.1 構造方式

Response(data, status=None, template_name=None, headers=None, content_type=None)

data數據不要是render處理之後的數據,只需傳遞python的內建類型數據即可,REST framework會使用renderer渲染器處理data

data不能是複雜結構的數據,如Django的模型類對象,對於這樣的數據我們可以使用Serializer序列化器序列化處理後(轉爲了Python字典類型)再傳遞給data參數。

參數說明:

  • data: 爲響應準備的序列化處理後的數據;
  • status: 狀態碼,默認200;
  • template_name: 模板名稱,如果使用HTMLRenderer 時需指明;
  • headers: 用於存放響應頭信息的字典;
  • content_type: 響應數據的Content-Type,通常此參數無需傳遞,REST framework會根據前端所需類型數據來設置該參數。

1.2.2 常用屬性

1).data

傳給response對象的序列化後,但尚未render處理的數據

2).status_code

狀態碼的數字

3).content

經過render處理後的響應數據

1.2.3 狀態碼

爲了方便設置狀態碼,REST framewrok在rest_framework.status模塊中提供了常用狀態碼常量。

1)信息告知 - 1xx
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
2)成功 - 2xx
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
3)重定向 - 3xx
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
4)客戶端錯誤 - 4xx
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
5)服務器錯誤 - 5xx
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED

2 視圖

REST framework 提供了衆多的通用視圖基類與擴展類,以簡化視圖的編寫。

2.1 2個視圖基類

2.1.1 APIView

rest_framework.views.APIView

APIView是REST framework提供的所有視圖的基類,繼承自Django的View父類。

APIViewView的不同之處在於:

  • 傳入到視圖方法中的是REST framework的Request對象,而不是Django的HttpRequeset對象;
  • 視圖方法可以返回REST framework的Response對象,視圖會爲響應數據設置(render)符合前端要求的格式;
  • 任何APIException異常都會被捕獲到,並且處理成合適的響應信息;
  • 在進行dispatch()分發前,會對請求進行身份認證、權限檢查、流量控制。

支持定義的類屬性

  • authentication_classes 列表或元祖,身份認證類
  • permissoin_classes 列表或元祖,權限檢查類
  • throttle_classes 列表或元祖,流量控制類

APIView中仍以常規的類視圖定義方法來實現get() 、post() 或者其他請求方式的方法。

舉例:

from rest_framework.views import APIView
from rest_framework.response import Response

# url(r'^students/$', views.StudentsAPIView.as_view()),
class StudentsAPIView(APIView):
  	
    def get(self, request):
        data_list = Student.objects.all()
        serializer = StudentModelSerializer(instance=data_list, many=True)
        return Response(serializer.data)

2.1.2 GenericAPIView[通用視圖類]

rest_framework.generics.GenericAPIView

繼承自APIVIew主要增加了操作序列化器和數據庫查詢的方法,作用是爲下面Mixin擴展類的執行提供方法支持。通常在使用時,可搭配一個或多個Mixin擴展類。

提供的關於序列化器使用的屬性與方法

  • 屬性:

    • serializer_class 指明視圖使用的序列化器
  • 方法:

    • get_serializer_class(self)

      當出現一個視圖類中調用多個序列化器時,那麼可以通過條件判斷在get_serializer_class方法中通過返回不同的序列化器類名就可以讓視圖方法執行不同的序列化器對象了。

      返回序列化器類,默認返回serializer_class,可以重寫,例如:

      def get_serializer_class(self):
          if self.request.user.is_staff:
              return FullAccountSerializer
          return BasicAccountSerializer
      
    • get_serializer(self, *args, **kwargs)

      返回序列化器對象,主要用來提供給Mixin擴展類使用,如果我們在視圖中想要獲取序列化器對象,也可以直接調用此方法。

      注意,該方法在提供序列化器對象的時候,會向序列化器對象的context屬性補充三個數據:request、format、view,這三個數據對象可以在定義序列化器時使用。

      • request 當前視圖的請求對象
      • view 當前請求的類視圖對象
      • format 當前請求期望返回的數據格式

提供的關於數據庫查詢的屬性與方法

  • 屬性:

    • queryset 指明使用的數據查詢集
  • 方法:

    • get_queryset(self)

      返回視圖使用的查詢集,主要用來提供給Mixin擴展類使用,是列表視圖與詳情視圖獲取數據的基礎,默認返回queryset屬性,可以重寫,例如:

      def get_queryset(self):
          user = self.request.user
          return user.accounts.all()
      
    • get_object(self)

      返回詳情視圖所需的模型類數據對象,主要用來提供給Mixin擴展類使用。

      在試圖中可以調用該方法獲取詳情信息的模型類對象。

      若詳情訪問的模型類對象不存在,會返回404。

      該方法會默認使用APIView提供的check_object_permissions方法檢查當前對象是否有權限被訪問。

      舉例:

      # url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
      class BookDetailView(GenericAPIView):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request, pk):
              book = self.get_object() # get_object()方法根據pk參數查找queryset中的數據對象
              serializer = self.get_serializer(book)
              return Response(serializer.data)
      

其他可以設置的屬性

  • pagination_class 指明分頁控制類
  • filter_backends 指明過濾控制後端

爲了方便學習上面的GenericAPIView通用視圖類,我們新建一個子應用。

python manage.py startapp gen

代碼:

from rest_framework.generics import GenericAPIView

from students.models import Student
from .serializers import StudentModelSerializer, StudentModel2Serializer
from rest_framework.response import Response

class StudentsGenericAPIView(GenericAPIView):
    # 本次視圖類中要操作的數據[必填]
    queryset = Student.objects.all()
    # 本次視圖類中要調用的默認序列化器[玄天]
    serializer_class = StudentModelSerializer

    def get(self, request):
        """獲取所有學生信息"""
        serializer = self.get_serializer(instance=self.get_queryset(), many=True)

        return Response(serializer.data)

    def post(self,request):

        data = request.data

        serializer = self.get_serializer(data=data)

        serializer.is_valid(raise_exception=True)

        instance = serializer.save()

        serializer = self.get_serializer(instance=instance)

        return Response(serializer.data)


class StudentGenericAPIView(GenericAPIView):
    queryset = Student.objects.all()

    serializer_class = StudentModelSerializer

    def get_serializer_class(self):
        """重寫獲取序列化器類的方法"""
        if self.request.method == "GET":
            return StudentModel2Serializer
        else:
            return StudentModelSerializer

    # 在使用GenericAPIView視圖獲取或操作單個數據時,視圖方法中的代表主鍵的參數最好是pk
    def get(self,request,pk):
        """獲取一條數據"""
        serializer = self.get_serializer(instance=self.get_object())

        return Response(serializer.data)

    def put(self,request,pk):

        data = request.data

        serializer = self.get_serializer(instance=self.get_object(),data=data)

        serializer.is_valid(raise_exception=True)

        serializer.save()

        serializer = self.get_serializer(instance=self.get_object())

        return Response(serializer.data)

序列化器類:

from rest_framework import serializers

from students.models import Student

class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model= Student
        fields = "__all__"


class StudentModel2Serializer(serializers.ModelSerializer):
    class Meta:
        model= Student
        fields = ("name","class_null")

2.2 5個視圖擴展類

作用:

提供了幾種後端視圖(對數據資源進行曾刪改查)處理流程的實現,如果需要編寫的視圖屬於這五種,則視圖可以通過繼承相應的擴展類來複用代碼,減少自己編寫的代碼量。

這五個擴展類需要搭配GenericAPIView父類,因爲五個擴展類的實現需要調用GenericAPIView提供的序列化器與數據庫查詢的方法。

1)ListModelMixin

列表視圖擴展類,提供list(request, *args, **kwargs)方法快速實現列表視圖,返回200狀態碼。

該Mixin的list方法會對數據進行過濾和分頁。

源代碼:

class ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        # 過濾
        queryset = self.filter_queryset(self.get_queryset())
        # 分頁
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        # 序列化
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

舉例:

from rest_framework.mixins import ListModelMixin

class BookListView(ListModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request):
        return self.list(request)

2)CreateModelMixin

創建視圖擴展類,提供create(request, *args, **kwargs)方法快速實現創建資源的視圖,成功返回201狀態碼。

如果序列化器對前端發送的數據驗證失敗,返回400錯誤。

源代碼:

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        # 獲取序列化器
        serializer = self.get_serializer(data=request.data)
        # 驗證
        serializer.is_valid(raise_exception=True)
        # 保存
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

3)RetrieveModelMixin

詳情視圖擴展類,提供retrieve(request, *args, **kwargs)方法,可以快速實現返回一個存在的數據對象。

如果存在,返回200, 否則返回404。

源代碼:

class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        # 獲取對象,會檢查對象的權限
        instance = self.get_object()
        # 序列化
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

舉例:

class BookDetailView(RetrieveModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request, pk):
        return self.retrieve(request)

4)UpdateModelMixin

更新視圖擴展類,提供update(request, *args, **kwargs)方法,可以快速實現更新一個存在的數據對象。

同時也提供partial_update(request, *args, **kwargs)方法,可以實現局部更新。

成功返回200,序列化器校驗數據失敗時,返回400錯誤。

源代碼:

class UpdateModelMixin(object):
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

5)DestroyModelMixin

刪除視圖擴展類,提供destroy(request, *args, **kwargs)方法,可以快速實現刪除一個存在的數據對象。

成功返回204,不存在返回404。

源代碼:

class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

使用GenericAPIView和視圖擴展類,實現api接口,代碼:

"""GenericAPIView結合視圖擴展類實現api接口"""
from rest_framework.mixins import ListModelMixin,CreateModelMixin
class Students2GenericAPIView(GenericAPIView,ListModelMixin,CreateModelMixin):
    # 本次視圖類中要操作的數據[必填]
    queryset = Student.objects.all()
    # 本次視圖類中要調用的默認序列化器[玄天]
    serializer_class = StudentModelSerializer

    def get(self, request):
        """獲取多個學生信息"""
        return self.list(request)

    def post(self,request):
        """添加學生信息"""
        return self.create(request)


from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student2GenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()

    serializer_class = StudentModelSerializer

    # 在使用GenericAPIView視圖獲取或操作單個數據時,視圖方法中的代表主鍵的參數最好是pk
    def get(self,request,pk):
        """獲取一條數據"""
        return self.retrieve(request,pk)

    def put(self,request,pk):
        """更新一條數據"""
        return self.update(request,pk)

    def delete(self,request,pk):
        """刪除一條數據"""
        return self.destroy(request,pk)

2.3 GenericAPIView的視圖子類

1)CreateAPIView

提供 post 方法

繼承自: GenericAPIView、CreateModelMixin

2)ListAPIView

提供 get 方法

繼承自:GenericAPIView、ListModelMixin

3)RetrieveAPIView

提供 get 方法

繼承自: GenericAPIView、RetrieveModelMixin

4)DestoryAPIView

提供 delete 方法

繼承自:GenericAPIView、DestoryModelMixin

5)UpdateAPIView

提供 put 和 patch 方法

繼承自:GenericAPIView、UpdateModelMixin

6)RetrieveUpdateAPIView

提供 get、put、patch方法

繼承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

7)RetrieveUpdateDestoryAPIView

提供 get、put、patch、delete方法

繼承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

3 視圖集ViewSet

使用視圖集ViewSet,可以將一系列邏輯相關的動作放到一個類中:

  • list() 提供一組數據
  • retrieve() 提供單個數據
  • create() 創建數據
  • update() 保存數據
  • destory() 刪除數據

ViewSet視圖集類不再實現get()、post()等方法,而是實現動作 action 如 list() 、create() 等。

視圖集只在使用as_view()方法的時候,纔會將action動作與具體請求方式對應上。如:

class BookInfoViewSet(viewsets.ViewSet):

    def list(self, request):
        books = BookInfo.objects.all()
        serializer = BookInfoSerializer(books, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        try:
            books = BookInfo.objects.get(id=pk)
        except BookInfo.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        serializer = BookInfoSerializer(books)
        return Response(serializer.data)

在設置路由時,我們可以如下操作

urlpatterns = [
    url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
    url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]

3.1 常用視圖集父類

1) ViewSet

繼承自APIViewViewSetMixin,作用也與APIView基本類似,提供了身份認證、權限校驗、流量管理等。

ViewSet主要通過繼承ViewSetMixin來實現在調用as_view()時傳入字典(如{‘get’:‘list’})的映射處理工作。

在ViewSet中,沒有提供任何動作action方法,需要我們自己實現action方法。

2)GenericViewSet

使用ViewSet通常並不方便,因爲list、retrieve、create、update、destory等方法都需要自己編寫,而這些方法與前面講過的Mixin擴展類提供的方法同名,所以我們可以通過繼承Mixin擴展類來複用這些方法而無需自己編寫。但是Mixin擴展類依賴與GenericAPIView,所以還需要繼承GenericAPIView

GenericViewSet就幫助我們完成了這樣的繼承工作,繼承自GenericAPIViewViewSetMixin,在實現了調用as_view()時傳入字典(如{'get':'list'})的映射處理工作的同時,還提供了GenericAPIView提供的基礎方法,可以直接搭配Mixin擴展類使用。

舉例:

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

url的定義

urlpatterns = [
    path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})),

]

3)ModelViewSet

繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

4)ReadOnlyModelViewSet

繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin。

3.2 視圖集中定義附加action動作

在視圖集中,除了上述默認的方法動作外,還可以添加自定義動作。

舉例:

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def login(self,request):
        """學生登錄功能"""
        return Response({"message":"登錄成功"})

url的定義

urlpatterns = [
    path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students8/(?P<pk>\d+)/",
            views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),

    path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"}))

]

3.3 action屬性

在視圖集中,我們可以通過action對象屬性來獲取當前請求視圖集時的action動作是哪個。

例如:

from rest_framework.viewsets import ModelViewSet
from students.models import Student
from .serializers import StudentModelSerializer
from rest_framework.response import Response
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def get_new_5(self,request):
        """獲取最近添加的5個學生信息"""
        # 操作數據庫
        print(self.action) # 獲取本次請求的視圖方法名
        
        
通過路由訪問到當前方法中.可以看到本次的action就是請求的方法名


9. 路由Routers

對於視圖集ViewSet,我們除了可以自己手動指明請求方式與動作action之間的對應關係外,還可以使用Routers來幫助我們快速實現路由信息。

REST framework提供了兩個router

  • SimpleRouter
  • DefaultRouter

1 使用方法

1) 創建router對象,並註冊視圖集,例如

from rest_framework import routers

router = routers.DefaultRouter()
router.register(r'router_stu', StudentModelViewSet, base_name='student')

register(prefix, viewset, base_name)

  • prefix 該視圖集的路由前綴
  • viewset 視圖集
  • base_name 路由別名的前綴

如上述代碼會形成的路由如下:

^books/$    name: book-list
^books/{pk}/$   name: book-detail

2)添加路由數據

可以有兩種方式:

urlpatterns = [
    ...
]
urlpatterns += router.urls

urlpatterns = [
    ...
    url(r'^', include(router.urls))
]

使用路由類給視圖集生成了路由地址

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def login(self,request):
        """學生登錄功能"""
        print(self.action)
        return Response({"message":"登錄成功"})


路由代碼:

from django.urls import path, re_path
from . import views
urlpatterns = [
    ...
]

"""使用drf提供路由類router給視圖集生成路由列表"""
# 實例化路由類
# drf提供一共提供了兩個路由類給我們使用,他們用法一致,功能幾乎一樣
from rest_framework.routers import DefaultRouter
router = DefaultRouter()

# 註冊視圖集
# router.register("路由前綴",視圖集類)
router.register("router_stu",views.StudentModelViewSet)

# 把生成的路由列表追加到urlpatterns
print( router.urls )
urlpatterns += router.urls



上面的代碼就成功生成了路由地址[增/刪/改/查一條/查多條的功能],但是不會自動我們在視圖集自定義方法的路由。

所以我們如果也要給自定義方法生成路由,則需要進行action動作的聲明。

2 視圖集中附加action的聲明

在視圖集中,如果想要讓Router自動幫助我們爲自定義的動作生成路由信息,需要使用rest_framework.decorators.action裝飾器。

以action裝飾器裝飾的方法名會作爲action動作名,與list、retrieve等同。

action裝飾器可以接收兩個參數:

  • methods: 聲明該action對應的請求方式,列表傳遞

  • detail
    聲明該action的路徑是否與單一資源對應,及是否是
    xxx/<pk>/action方法名/
    
    
    • True 表示路徑格式是xxx/<pk>/action方法名/
    • False 表示路徑格式是xxx/action方法名/

舉例:

from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action

class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # methods 設置當前方法允許哪些http請求訪問當前視圖方法
    # detail 設置當前視圖方法是否是操作一個數據
    # detail爲True,表示路徑名格式應該爲 router_stu/{pk}/login/
    @action(methods=['get'], detail=True)
    def login(self, request,pk):
        """登錄"""
        ...

    # detail爲False 表示路徑名格式應該爲 router_stu/get_new_5/
    @action(methods=['put'], detail=False)
    def get_new_5(self, request):
        """獲取最新添加的5個學生信息"""
        ...

由路由器自動爲此視圖集自定義action方法形成的路由會是如下內容:

^router_stu/get_new_5/$    name: router_stu-get_new_5
^router_stu/{pk}/login/$   name: router_stu-login

3 路由router形成URL的方式

1) SimpleRouter
在這裏插入圖片描述

2)DefaultRouter
在這裏插入圖片描述

DefaultRouter與SimpleRouter的區別是,DefaultRouter會多附帶一個默認的API根視圖,返回一個包含所有列表視圖的超鏈接響應數據。

爲了方便接下來的學習,我們創建一個新的子應用 opt

python manage.py startapp opt

因爲接下來的功能中需要使用到登陸功能,所以我們使用django內置admin站點並創建一個管理員.

python manage.py createsuperuser

在這裏插入圖片描述

創建管理員以後,訪問admin站點,先修改站點的語言配置

settings.py


在這裏插入圖片描述

訪問admin 站點效果:
在這裏插入圖片描述

10. 認證Authentication

可以在配置文件中配置全局默認的認證方案

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',  # session認證
        'rest_framework.authentication.BasicAuthentication',   # 基本認證
    )
}

也可以在每個視圖中通過設置authentication_classess屬性來設置

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView

class ExampleView(APIView):
    # 類屬性
    authentication_classes = [SessionAuthentication, BasicAuthentication]
    ...

認證失敗會有兩種可能的返回值:

  • 401 Unauthorized 未認證
  • 403 Permission Denied 權限被禁止

11. 權限Permissions

權限控制可以限制用戶對於視圖的訪問和對於具體數據對象的訪問。

  • 在執行視圖的dispatch()方法前,會先進行視圖訪問權限的判斷
  • 在通過get_object()獲取具體對象時,會進行模型對象訪問權限的判斷

使用

可以在配置文件中全局設置默認的權限管理類,如

REST_FRAMEWORK = {
    ....
    
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}

如果未指明,則採用如下默認配置

'DEFAULT_PERMISSION_CLASSES': (
   'rest_framework.permissions.AllowAny',
)

也可以在具體的視圖中通過permission_classes屬性來設置,如

from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = (IsAuthenticated,)
    ...

提供的權限

  • AllowAny 允許所有用戶
  • IsAuthenticated 僅通過認證的用戶
  • IsAdminUser 僅管理員用戶
  • IsAuthenticatedOrReadOnly 已經登陸認證的用戶可以對數據進行增刪改操作,沒有登陸認證的只能查看數據。

舉例

from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView

class StudentAPIView(RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]

自定義權限

如需自定義權限,需繼承rest_framework.permissions.BasePermission父類,並實現以下兩個任何一個方法或全部

  • .has_permission(self, request, view)

    是否可以訪問視圖, view表示當前視圖對象

  • .has_object_permission(self, request, view, obj)

    是否可以訪問數據對象, view表示當前視圖, obj爲數據對象

例如:

在當前子應用下,創建一個權限文件permissions.py中聲明自定義權限類:

from rest_framework.permissions import BasePermission

class IsXiaoMingPermission(BasePermission):
    def has_permission(self, request, view):
        if( request.user.username == "xiaoming" ):
            return True
from .permissions import IsXiaoMingPermission
class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = [IsXiaoMingPermission]

12. 限流Throttling

可以對接口訪問的頻次進行限制,以減輕服務器壓力。

一般用於付費購買次數,投票等場景使用.

使用

可以在配置文件中,使用DEFAULT_THROTTLE_CLASSESDEFAULT_THROTTLE_RATES進行全局配置,

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day'
    }
}

DEFAULT_THROTTLE_RATES 可以使用 second, minute, hourday來指明週期。

也可以在具體視圖中通過throttle_classess屬性來配置,如

from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class ExampleView(APIView):
    throttle_classes = (UserRateThrottle,)
    ...

可選限流類

1) AnonRateThrottle

限制所有匿名未認證用戶,使用IP區分用戶。

使用DEFAULT_THROTTLE_RATES['anon'] 來設置頻次

2)UserRateThrottle

限制認證用戶,使用User id 來區分。

使用DEFAULT_THROTTLE_RATES['user'] 來設置頻次

3)ScopedRateThrottle

限制用戶對於每個視圖的訪問頻次,使用ip或user id。

例如:

class ContactListView(APIView):
    throttle_scope = 'contacts'
    ...

class ContactDetailView(APIView):
    throttle_scope = 'contacts'
    ...

class UploadView(APIView):
    throttle_scope = 'uploads'
    ...
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.ScopedRateThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
        'contacts': '1000/day',
        'uploads': '20/day'
    }
}

實例

全局配置中設置訪問頻率

    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/minute',
        'user': '10/minute'
    }
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView
from rest_framework.throttling import UserRateThrottle

class StudentAPIView(RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]
    throttle_classes = (UserRateThrottle,)

13. 過濾Filtering

對於列表數據可能需要根據字段進行過濾,我們可以通過添加django-fitlter擴展來增強支持。

pip install django-filter

在配置文件中增加過濾後端的設置:

INSTALLED_APPS = [
    ...
    'django_filters',  # 需要註冊應用,
]

REST_FRAMEWORK = {
    ...
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

在視圖中添加filter_fields屬性,指定可以過濾的字段

class StudentListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    filter_fields = ('age', 'sex')

# 127.0.0.1:8000/four/students/?sex=1

14. 排序

對於列表數據,REST framework提供了OrderingFilter過濾器來幫助我們快速指明數據按照指定字段進行排序。

使用方法:

在類視圖中設置filter_backends,使用rest_framework.filters.OrderingFilter過濾器,REST framework會在請求的查詢字符串參數中檢查是否包含了ordering參數,如果包含了ordering參數,則按照ordering參數指明的排序字段對數據集進行排序。

前端可以傳遞的ordering參數的可選字段值需要在ordering_fields中指明。

示例:

class StudentListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_backends = [OrderingFilter]
    ordering_fields = ('id', 'age')

# 127.0.0.1:8000/books/?ordering=-age
# -id 表示針對id字段進行倒序排序
# id  表示針對id字段進行升序排序

如果需要在過濾以後再次進行排序,則需要兩者結合!

from rest_framework.generics import ListAPIView
from students.models import Student
from .serializers import StudentModelSerializer
from django_filters.rest_framework import DjangoFilterBackend
class Student3ListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_fields = ('age', 'sex')
    # 因爲局部配置會覆蓋全局配置,所以需要重新把過濾組件核心類再次聲明,
    # 否則過濾功能會失效
    filter_backends = [OrderingFilter,DjangoFilterBackend]
    ordering_fields = ('id', 'age')

15. 分頁Pagination

REST framework提供了分頁的支持。

我們可以在配置文件中設置全局的分頁方式,如:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100  # 每頁數目
}

也可通過自定義Pagination類,來爲視圖添加不同分頁行爲。在視圖中通過pagination_clas屬性來指明。

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 1000
    page_size_query_param = 'page_size'
    max_page_size = 10000
class BookDetailView(RetrieveAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    pagination_class = LargeResultsSetPagination

注意:如果在視圖內關閉分頁功能,只需在視圖內設置

pagination_class = None

可選分頁器

1) PageNumberPagination

前端訪問網址形式:

GET  http://127.0.0.1:8000/students/?page=4

可以在子類中定義的屬性:

  • page_size 每頁數目
  • page_query_param 前端發送的頁數關鍵字名,默認爲"page"
  • page_size_query_param 前端發送的每頁數目關鍵字名,默認爲None
  • max_page_size 前端最多能設置的每頁數量
# 聲明分頁的配置類
from rest_framework.pagination import PageNumberPagination
class StandardPageNumberPagination(PageNumberPagination):
    # 默認每一頁顯示的數據量
    page_size = 2
    # 允許客戶端通過get參數來控制每一頁的數據量
    page_size_query_param = "size"
    max_page_size = 10
    # 自定義頁碼的參數名
    page_query_param = "p"

class StudentAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    pagination_class = StandardPageNumberPagination

# 127.0.0.1/four/students/?p=1&size=5

2)LimitOffsetPagination

前端訪問網址形式:

GET http://127.0.0.1/four/students/?limit=100&offset=400

可以在子類中定義的屬性:

  • default_limit 默認限制,默認值與PAGE_SIZE設置一直
  • limit_query_param limit參數名,默認’limit’
  • offset_query_param offset參數名,默認’offset’
  • max_limit 最大limit限制,默認None
from rest_framework.pagination import LimitOffsetPagination
class StandardLimitOffsetPagination(LimitOffsetPagination):
    # 默認每一頁查詢的數據量,類似上面的page_size
    default_limit = 2
    limit_query_param = "size"
    offset_query_param = "start"

class StudentAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 調用頁碼分頁類
    # pagination_class = StandardPageNumberPagination
    # 調用查詢偏移分頁類
    pagination_class = StandardLimitOffsetPagination

16. 異常處理 Exceptions

REST framework提供了異常處理,我們可以自定義異常處理函數。

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    # 先調用REST framework默認的異常處理方法獲得標準錯誤響應對象
    response = exception_handler(exc, context)

    # 在此處補充自定義的異常處理
    if response is None:
        response.data['status_code'] = response.status_code

    return response

在配置文件中聲明自定義的異常處理

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}

如果未聲明,會採用默認的方式,如下

rest_frame/settings.py

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}

例如:

補充上處理關於數據庫的異常

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError

def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if response is None:
        view = context['view']
        if isinstance(exc, DatabaseError):
            print('[%s]: %s' % (view, exc))
            response = Response({'detail': '服務器內部錯誤'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response

REST framework定義的異常

  • APIException 所有異常的父類
  • ParseError 解析錯誤
  • AuthenticationFailed 認證失敗
  • NotAuthenticated 尚未認證
  • PermissionDenied 權限決絕
  • NotFound 未找到
  • MethodNotAllowed 請求方式不支持
  • NotAcceptable 要獲取的數據格式不支持
  • Throttled 超過限流次數
  • ValidationError 校驗失敗

也就是說,很多的沒有在上面列出來的異常,就需要我們在自定義異常中自己處理了。

17. 自動生成接口文檔

REST framework可以自動幫助我們生成接口文檔。

接口文檔以網頁的方式呈現。

自動接口文檔能生成的是繼承自APIView及其子類的視圖。

1. 安裝依賴

REST framewrok生成接口文檔需要coreapi庫的支持。

pip install coreapi

2. 設置接口文檔訪問路徑

在總路由中添加接口文檔路徑。

文檔路由對應的視圖配置爲rest_framework.documentation.include_docs_urls

參數title爲接口文檔網站的標題。

from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    path('docs/', include_docs_urls(title='站點頁面標題'))
]

3. 文檔描述說明的定義位置

1) 單一方法的視圖,可直接使用類視圖的文檔字符串,如

class BookListView(generics.ListAPIView):
    """
    返回所有圖書信息.
    """

2)包含多個方法的視圖,在類視圖的文檔字符串中,分開方法定義,如

class BookListCreateView(generics.ListCreateAPIView):
    """
    get:
    返回所有圖書信息.

    post:
    新建圖書.
    """

3)對於視圖集ViewSet,仍在類視圖的文檔字符串中封開定義,但是應使用action名稱區分,如

class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
    """
    list:
    返回圖書列表數據

    retrieve:
    返回圖書詳情數據

    latest:
    返回最新的圖書數據

    read:
    修改圖書的閱讀量
    """

4. 訪問接口文檔網頁

瀏覽器訪問 127.0.0.1:8000/docs/,即可看到自動生成的接口文檔。
在這裏插入圖片描述

兩點說明:

1) 視圖集ViewSet中的retrieve名稱,在接口文檔網站中叫做read

2)參數的Description需要在模型類或序列化器類的字段中以help_text選項定義,如:

class Student(models.Model):
    ...
    age = models.IntegerField(default=0, verbose_name='年齡', help_text='年齡')
    ...

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
        extra_kwargs = {
            'age': {
                'required': True,
                'help_text': '年齡'
            }
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章