Django REST framework完全入門

403·Forbidden發表於2021-12-09

Django REST framework 一個強大靈活的Django工具包,提供了便捷的 REST API 開發框架
我們用傳統的django也可以實現REST風格的api,但是頂不住Django REST framework實現起來太簡單了

什麼REST API風格

主要需要注意這幾個:

  1. api部署的域名, 主域名或者專有域名
  2. 版本, 通過url地址或者請求頭accept
  3. 路徑, 只能有名詞, 不能有動詞
  4. http請求動詞, get, post, update, delete
  5. 狀態, 200, 201, 204, 400, 401, 403, 404
  6. 返回json格式資料

例子(僅作參考):

功能 路徑 請求方式 響應狀態碼(成功) 響應體(成功)
獲取所有的書籍 /books get 200 OK 全部書籍
建立書籍 /books post 201 Created 已經建立的書籍
獲取單個書籍 /books/{id} get 200 OK 單個書籍
修改書籍 /books/{id} put 201 Created 已經修改的單個書籍
刪除書籍 /books/{id} delete 204 No Content

詳細的可以看這裡:RESTful 架構詳解
關於返回什麼,可以看這裡:通用設計規則

簡單使用流程

在我看來,Django RST framework的主要工作就是對傳統django中的viewform在進行了通用性設計。由於使用REST API,我們可以統一使用一樣的請求方式,請求引數和請求體,而響應體和狀態碼也是確定。所以Django RST framework的複用性極高,而且Django RST framework提供了從簡單到複雜的封裝類,我們只需要直接使用這些類或繼承這些類重寫部分方法,就可以享受到便利性和可擴充套件性。
為了顯示Django RST framework簡單易用,用展示封裝程度比較高的類實現一個五個基本功能(get一個、get全部、post建立、put修改單個、delete刪除單個)

  1. 安裝Django RST framework

    $pip install djangorestframework
    
  2. 修改setting檔案
    settings.INSTALLED_APPS中新增rest_framework

    注:新增這個app可以為我們提供測試頁面, 假如是使用postman等工具進行測試的話可以不用新增

  3. 建立model

    from django.db import models
    
    
    class Author(models.Model):
        aid = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32, verbose_name="姓名")
        phone = models.CharField(max_length=11, verbose_name="手機號碼")
        address = models.CharField(max_length=63, verbose_name="地址")
        def __str__(self):
            return self.name
    
    
  4. 執行遷移命令

    $python manage.py makemigrations
    $python manage.py migrate
    
  5. 定義序列化器
    在app目錄下建立一個serializers.py,在裡面定義序列化器:

    from rest_framework.serializers import ModelSerializer
    from rest_test.models import Author
    
    
    class AuthorSerialize(ModelSerializer):
        class Meta:
            model = Author
            fields = "__all__"
    
    
  6. 使用檢視集
    views.py中,匯入並使用之前的模型和序列化器:

    from rest_framework.viewsets import ModelViewSet
    
    from rest_test.serializers import AuthorSerialize
    from rest_test.models import Author
    
    
    class AuthorModelViewSet(ModelViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorSerialize
    
    
  7. 定義路由規則
    在根路由中定義urlpatterns:

    """djangotest URL Configuration
    """
    from django.contrib import admin
    from django.urls import path
    from rest_test import views
    from rest_framework.routers import SimpleRouter
    
    urlpatterns = [
    	path('admin/', admin.site.urls),
    
    ]
    
    
    router = SimpleRouter()
    
    # 新增自己的url
    router.register('authors', views.AuthorModelViewSet, basename="authors")
    urlpatterns += router.urls
    
    
  8. 啟動django,並訪問/authors
    我們就可以看到這樣的頁面,可以對資料進行增刪改查了:
    示意圖

序列化器

序列化器的主要作用就是進行資料轉換,即序列化(python的資料型別轉換為json等資料型別)和反序列化(json等資料型別轉換為python的資料型別)
在反序列化的過程中我們還可以手動驗證資料是否合法, 可以與model相結合,完成對資料庫的操作
所以說,序列化器和djangoForm非常像,所以假如會djangoForm的話,應該很好理解

普通的序列化器

在定程度上可以類比dango.forms.Form

定義起來非常簡單,欄位名稱對應的是model中的欄位名,欄位型別和model中的欄位有一定的對應關係,引數是一些約束條件和描述資訊, 見: 欄位和引數

from rest_framework import serializers


class AuthorSerialize(serializers.Serializer):
    aid = serializers.IntegerField(read_only=True,label="作者id")
    name = serializers.CharField(max_length=32, label="姓名")
    phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
    address = serializers.CharField(max_length=63, label="地址")

欄位和引數

欄位

這裡的引數指的是欄位自帶的引數,不是像required這樣都可以用的引數
僅列出常用的

全部欄位都是rest_framework.serializers下的

欄位名稱 描述 引數 備註
BooleanField 布林值 無論有無指定,預設值都為False, NullBooleanField可以接收None的值
CharField 文字 max_length min_length allow_blank(空字串是否為有效值)trim_whitespace(True時修剪前後的空白,預設為True
EmailField 電子郵箱 max_length min_length allow_blank(預設為False
RegexField 文字,通過正則匹配 regex(正規表示式) max_length min_length allow_blank(預設為False
SlugField regex[a-zA-Z0-9_-]+RegexField欄位 就比RegexField少了regex引數
URLField http://<host>/<path>格式的RegexField SlugField
UUIDField uuid欄位 format 該欄位的format引數指定的是uuid的格式,見UUIDField
IntegerField 整數 max_value min_value
FloatField 小數 max_value min_value
DateTimeField 時間 年月日時分秒 format(序列化的時間格式) input_formats(反序列化的時間格式) default_timezone(時區)

引數

form一樣,這裡指的是通用的引數,不是欄位自帶的引數

引數 描述
read_only 只讀,不能反序列化,如主鍵,預設是False
Write_only 只寫,不能序列化,只在更新和建立資料時使用,預設是False
required 反序列化必須提供的欄位,預設為True
default 預設值,在沒有提供值時使用,預設不設定
allow_null 允許值為None(一般情況下,如果向序列化欄位傳遞None是會丟擲異常的),預設是False
validators 指定驗證器列表
error_message 錯誤程式碼到錯誤訊息的字典
label 提供可讀性高的欄位名稱
help_text 對欄位進行詳細描述

全部欄位和引數可以檢視官方文件:Serializer fields (中文)Serializer fields (英文)

序列化操作

書接上回,我們已經在serializers.py中定義了一個AuthorSerialize,下面就利用該序列化器進行序列化,即python的轉換為json等資料。

序列化一個

步驟:

  1. 匯入AuthorSerializeAuthor模型
  2. 使用get獲取Author的資料
  3. 將資料傳給AuthorSerializeinstance引數
  4. 使用.data屬性(是一個靜態屬性)獲得序列化後的資料

注意:假如你的資料庫中沒有資料,應該提前將資料建立好,可以用SQL命令也可以用django ORM或藉助其他工具

使用django shell執行命令:

$ python manage.py shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from rest_test.serializers import AuthorSerialize
>>> from rest_test.models import Author
>>>
>>> a = Author.objects.get(pk=4)
>>> t = AuthorSerialize(instance=a)
>>> t.data
{'aid': 4, 'name': '張三', 'phone': '1388888888', 'address': '廣東省廣州市xxxxx'}
>>>

暫時用命令列的方式序列化

序列化多個

序列化器一般是不能序列化多個資料的,需要我們指定引數many=True。同樣的,我們先使用django shell執行命令:

$ python manage.py shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from rest_test.serializers import AuthorSerialize
>>> from rest_test.models import Author
>>> 
>>> a = Author.objects.all()
>>> t = AuthorSerialize(instance=a, many=True)
>>> 
>>> t.data
[OrderedDict([('aid', 4), ('name', '張三'), ('phone', '138888888888'), ('address', '廣東省廣州市xxxx')]),
OrderedDict([('aid', 5), ('name', '李四'), ('phone', '1388888888'), ('address', '廣東省廣州市xxxxx')]), 
OrderedDict([('aid', 6),('name', '王五'), ('phone', '1388888888'), ('address', '廣東省廣州市xxxxx')])]

可以看到,data的值是一個OrderedDict資料型別

外來鍵序列化

由於外來鍵在序列化時不知道是使用主鍵、__str__或整個主鍵的資料中的一個,所以需要我們手動指定不同的欄位實現。
假如我們新增了一個model:

class Author(models.Model):
    aid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, verbose_name="姓名")
    phone = models.CharField(max_length=11, verbose_name="手機號碼")
    address = models.CharField(max_length=63, verbose_name="地址")

    def __str__(self):
        return self.name
        
        
class Books(models.Model):
    bid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, verbose_name="書名")
    description = models.CharField(max_length=128, verbose_name="書籍描述")
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

    def __str__(self):
        return f"{self.author.name}: 《{self.name}》"

子表序列化

子表序列化比較簡單,因為欄位定義子表哪邊,所以可以通過欄位,只需要確定返回主表的哪些值即可。

  • 返回外來鍵的主鍵PrimaryKeyRelatedField
    定義序列化器:

        class BookSerialize(serializers.Serializer):
            bid = serializers.IntegerField(read_only=True)
            name = serializers.CharField(max_length=32, label="書名")
            description = serializers.CharField(max_length=128, label="書籍描述")
            author = serializers.PrimaryKeyRelatedField(read_only=True)
    
    

    PrimaryKeyRelatedField必須指定read_only為True,或指定queryset=Books.objects.all(),否則會報錯

    使用django shell測試:

    $ python manage.py shell
    Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> from rest_test.serializers import BookSerialize
    >>> from rest_test.models import Books
    >>>
    >>> b = Books.objects.all()
    >>> s = BookSerialize(instance=b, many=True)
    >>> s.data
    [OrderedDict([('bid', 1), ('name', 'book1'), ('description', 'very good'), ('author', 4)]),
    OrderedDict([('bid', 2), ('name', 'book2'), ('description', '好書推薦'), ('author', 4)])]
    >>>
    
    
  • 返回外來鍵的__str__方法StringRelatedField
    修改author的欄位型別,讓它返回的是主表的__str__方法:

    class BookSerialize(serializers.Serializer):
        bid = serializers.IntegerField(read_only=True)
        name = serializers.CharField(max_length=32, label="書名")
        description = serializers.CharField(max_length=128, label="書籍描述")
        author = serializers.StringRelatedField(read_only=True)
    
    
    
    

    可以不設定read_only

    驗證:

    $ python manage.py shell
    Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> from rest_test.serializers import BookSerialize
    >>> from rest_test.models import Books
    >>> 
    >>> b = Books.objects.all()
    >>> s = BookSerialize(instance=b, many=True)
    >>> s.data
    [OrderedDict([('bid', 1), ('name', 'book1'), ('description', 'very good'), ('author', '張三')]), 
    OrderedDict([('bid', 2), ('name', 'book2'), ('description', '好書推薦'), ('author', '張三')])]
    
  • 返回整個主表的資料
    只需要欄位的值改為主表的序列化器即可:

    class AuthorSerialize(serializers.Serializer):
        aid = serializers.IntegerField(read_only=True)
        name = serializers.CharField(max_length=32, label="姓名")
        phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
        address = serializers.CharField(max_length=63, label="地址")
    
    
    class BookSerialize(serializers.Serializer):
        bid = serializers.IntegerField(read_only=True)
        name = serializers.CharField(max_length=32, label="書名")
        description = serializers.CharField(max_length=128, label="書籍描述")
        author = AuthorSerialize(read_only=True)
    
    

    驗證:

    $ python manage.py shell
    Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> from rest_test.serializers import AuthorSerialize, BookSerialize
    >>> from rest_test.models import Author, Books
    >>> 
    >>> b = Books.objects.all()
    >>> s = BookSerialize(instance=b, many=True)
    >>> s.data
    [OrderedDict([('bid', 1), ('name', 'book1'), ('description', 'very good'), 
    ('author', OrderedDict([('aid', 4), ('name', '張三'), ('phone', '138888888888'), ('address', '廣東省廣州市xxxxx')]))]), 
    OrderedDict([('bid', 2), ('name', 'book2'), ('description', '好書推薦'), 
    ('author', OrderedDict([('aid', 4), ('name', '張三'), ('phone', '138888888888'), ('address', '廣東省廣州市xxxxx')]))])]
    

主表序列化

由於主表不能直接訪問子表,但是我們知道django為我們提供了表名(小寫)_set管理器,這樣我們就可以通過該管理器訪問子表了。

假如指定了related_name引數的話,該管理器的名稱就是related_name的值
如可以改成這樣:author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="author")
另,關於管理器,可以檢視django的文件:“反向” 關聯管理器

為了演示,使用預設的管理器

與子表類似,我們需要指定返回什麼值(__str__或主鍵),使用的是相同的欄位,但是要指定many引數!!

  • __str__ StringRelatedField

    注意many引數

    序列化器:

    class AuthorSerialize(serializers.Serializer):
        aid = serializers.IntegerField(read_only=True)
        name = serializers.CharField(max_length=32, label="姓名")
        phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
        address = serializers.CharField(max_length=63, label="地址")
        books_set = serializers.StringRelatedField(read_only=True, many=True)
    
    

    演示:

    $ python manage.py shell
    Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> from rest_test.serializers import AuthorSerialize
    >>> from rest_test.models import Author
    >>> 
    >>> a = Author.objects.get(pk=4)
    >>> t = AuthorSerialize(instance=a)
    >>> t.data
    {'aid': 4, 'name': '張三', 'phone': '138888888888', 'address': '廣東省廣州市xxxxx', 'books_set': ['張三: 《book1》', '張三: 《book2》']}
    
  • 主鍵PrimaryKeyRelatedField

    注意many引數

    序列化器:

    class AuthorSerialize(serializers.Serializer):
        aid = serializers.IntegerField(read_only=True)
        name = serializers.CharField(max_length=32, label="姓名")
        phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
        address = serializers.CharField(max_length=63, label="地址")
        books_set = serializers.PrimaryKeyRelatedField(read_only=True,many=True)
    
    

    演示:

    $ python manage.py shell
    Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>> from rest_test.serializers import AuthorSerialize
    >>> from rest_test.models import Author
    >>> 
    >>> a = Author.objects.get(pk=4)
    >>>
    >>> t = AuthorSerialize(instance=a)
    >>> t.data
    {'aid': 4, 'name': '張三', 'phone': '138888888888', 'address': '廣東省廣州市xxxxx', 'books_set': [1, 2]}
    >>>
    

反序列化操作

上面演示瞭如何將model序列化成 字串或OrderedDict,而反序列化化就是將json等資料,變成序列化器中的欄位的資料型別,就好比我們使用form處理資料一樣。

一般操作

此部分還不涉及將資料儲存到資料庫

流程:

  1. 獲得資料
  2. 將資料傳入到序列化器的data引數中
  3. 呼叫返回物件的is_valid()方法

由於還未講到檢視,所以先用一個字典資料代替從瀏覽器傳過來的資料。

$python manage.py shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from rest_test.serializers import AuthorSerialize, BookSerialize
>>> from rest_test.models import Author, Books
>>>
>>> data = {
...     "name": "龍傲天",
...     "phone": "18888888888",
...     "address": "xxx省xx市",
... }
>>>
>>> result = AuthorSerialize(data=data)
>>> result.is_valid(raise_exception=True)
True

# ################ 這裡故意用一個錯誤的資料 #############################
>>> data = {
...     "name": "龍傲天",
...     "phone": "188888888889",
...     "address": "xxx省xx市",
... }
>>> result = AuthorSerialize(data=data)
>>> result.is_valid(raise_exception=True)
# 報錯
rest_framework.exceptions.ValidationError: {'phone': [ErrorDetail(string='Ensure this field has no more than 11 characters.', code='max_length')]}
>>>
  • is_validdjango formis_valid一樣,可以檢驗資料是否符合我們在序列化器中定義的一樣,檢驗成功是返回True,否則返回False
  • 假如指定raise_exceptionTrue,那麼當資料不合法時,丟擲異常,我們可以通過error_messages引數提示資訊(見: 自定義錯誤資訊)。
  • 假如不想要丟擲異常,我們也可以通過.errors獲取錯誤資訊(這是一個OrderedDict的子類物件)如這裡的result.errors

利用序列化器進行建立和更新資料

終於講到如何將資料儲存到資料庫了。

用過django ModelForm的都知道,只要將資料傳入到form中,假如資料通過,就可以通過.save()方法將資料儲存到資料庫,指定instance引數可一個更新資料。而序列化器的使用方式樣是這樣,不過我們現在用的是叫較為低階的序列化器,需要我們自定義建立資料和更新資料的邏輯

建立資料

使用步驟:

  1. 獲得資料
  2. 將資料傳入到序列化器的data引數中
  3. 呼叫返回物件的is_valid()方法
  4. 為序列化器定義create方法
  5. 呼叫save方法,執行建立資料的函式(會呼叫create函式)

這裡的create函式需要我們自定義,在序列化器中:

class AuthorSerialize(serializers.Serializer):
    aid = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32, label="姓名")
    phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
    address = serializers.CharField(max_length=63, label="地址")
    books_set = serializers.StringRelatedField(read_only=True, many=True)

    def create(self, validated_data):
        """
        建立資料
        :param validated_data: 已經通過驗證的資料
        :return: Author instance
        """
        author = Author.objects.create(**validated_data)
        
        # 不要忘記返回資料了
        return author
        

可以說非常簡單,下面有django shell測試一下:

$python manage.py shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from rest_test.serializers import AuthorSerialize
>>> 
>>> data = {
...     "name": "龍傲天",
...     "phone": "18888888888",
...     "address": "xxx省xx市",
... }
>>> result = AuthorSerialize(data=data)
>>> result.is_valid(raise_exception=True)
True
>>> result.save()
<Author: 龍傲天>

用PyCharm自帶的Database工具,我們可以看到的確已經儲存到了資料庫中:
示意圖

更新資料(全部)

PUT請求方式是更新全部資料

使用步驟:

  1. 獲得資料
  2. 將資料傳入到序列化器的data引數中, 要更新的資料傳入instance引數
  3. 呼叫返回物件的is_valid()方法
  4. 為序列化器定義update方法
  5. 呼叫save方法,執行建立資料的函式(會呼叫update函式)

只要序列化器中同時傳入datainstance引數,它就會呼叫更新的方法(update)

注意:data引數傳入資料,instance引數傳入待更新的model物件

class AuthorSerialize(serializers.Serializer):
    aid = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32, label="姓名")
    phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
    address = serializers.CharField(max_length=63, label="地址")
    books_set = serializers.StringRelatedField(read_only=True, many=True)

    def update(self, instance, validated_data):
        """
        更新資料
        由於傳入的不是QuerySet物件,所以不能用update方法
        :param instance: Author物件
        :param validated_data: 已經驗證的資料
        :return: instance
        """
        instance.name = validated_data.get("name")
        instance.phone = validated_data.get("phone")
        instance.address = validated_data.get("address")
        instance.save()  # 不要忘記儲存了

        return instance

$ python manage.py shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from rest_test.serializers import AuthorSerialize
>>> from rest_test.models import Author
>>> 
>>> author = Author.objects.get(pk=10)
>>> 
>>> data = {
...     "name": "龍傲天",
...     "phone": "18888888888",
...     "address": "廣東省廣州市",
... }
>>> result = AuthorSerialize(data=data, instance=author)
>>> result.is_valid(raise_exception=True)
True
>>> result.save()
<Author: 龍傲天>
>>>

更新資料(部分)

PATCH請求方式是更新部分資料

上面我們只更新了地址的資訊,卻把其它不用改的資訊也一併穿了進去,這顯然不是我們預期的,但由於序列化器的欄位的required預設為True,除非我們設定read_only=True或指定一個預設值,否則會報錯,但設定了這些又會影響到我們正常建立資料的驗證。
所以,序列化器提供了一個引數:partial用來說明這是更新部分資訊的。在使用時和上面的一樣,只是多了個引數而已。不過我們需要修改一下update函式, 因為值可能為None, 會報錯,可以指定get的預設值,也可以用反射的方法:


class AuthorSerialize(serializers.Serializer):
    aid = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32, label="姓名")
    phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
    address = serializers.CharField(max_length=63, label="地址")
    books_set = serializers.StringRelatedField(read_only=True, many=True)

    def update(self, instance, validated_data):
        """
        更新資料
        :param instance: Author物件
        :param validated_data: 已經驗證的資料
        :return: instance
        """
        for field in validated_data:
            if not hasattr(instance, field):
                continue
            setattr(instance, field, validated_data.get(field))

        instance.save()  # 不要忘記儲存了
        return instance

來試一下吧:

$ python manage.py shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from rest_test.serializers import AuthorSerialize
>>> from rest_test.models import Author
>>> 
>>> data = {
...     "address": "廣東省廣州市xxxxx",
... }
>>> author = Author.objects.get(pk=10)
>>> result = AuthorSerialize(data=data, instance=author, partial=True)
>>> result.is_valid(raise_exception=True)
True
>>> result.save()
<Author: 龍傲天>

Model的序列化器

DRF有一個model序列化器: ModelSerializer, 其在一定程度上與dango.forms.ModelForm十分相似
在上面使用的例子就用到了該序列化器

說到底,它就是將幹了這幾件事:

  1. 我們的model欄位根據對應關係轉化為serializers中的欄位
  2. 將model欄位的約束條件變為serializers欄位的約束條件
  3. 內建了create方法和update方法

簡單使用

簡直是簡簡單單:

class AuthorModelSerialize(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = "__all__"

Meta類

從例子中看出,Meta類是關鍵,它決定著 要序列化的欄位、用哪個模型、錯誤資訊、約束條件等重要的引數。
下面我們看看Meta類可以指定什麼內容:

還是那句話,真的和ModelForm十分相似

  1. model
    指定用哪個Model
  2. fields
    使用模型中的哪些欄位,推薦利用元組顯式指定,但可以將值指定為__all__(表示全部欄位)
    不能和exclude一起使用,否則報錯:Cannot set both 'fields' and 'exclude' options on serializer xxx.
  3. exclude
    不使用模型中的哪些欄位
    不能和fields一起使用,否則報錯:Cannot set both 'fields' and 'exclude' options on serializer xxx.
  4. depth
    指定關聯深度,是一個數字
    注意,關聯欄位預設返回的是主鍵,即rest_framework.serializers.PrimaryKeyRelatedField
  5. read_only_fields
    只讀欄位,主鍵預設只讀
  6. extra_kwargs
    指定額外的引數,該引數可以傳給欄位

例子:

class AuthorModelSerialize(serializers.ModelSerializer):
    class Meta:
        model = Author      # 指定使用Author這個模型
        # 指定欄位,注意:books_set
        fields = ("aid", "name", "phone", "address", "books_set")

        # 不使用模型中的哪些欄位
        # 不能一起使用,這裡註釋掉
        
        # exclude = ("phone", )
        
        depth = 1       # 關聯深度為1
        # 設定只讀欄位
        read_only_fields = ("aid", "books_set")

        # 設定額外引數,該引數是給欄位的
        # 注意格式
        extra_kwargs = {
            "name": {
                "read_only": True
            }
        }

我們同樣用django shell檢視:

$ python manage.py shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from rest_test.serializers import AuthorModelSerialize
>>> from rest_test.models import Author
>>> 
>>> a = Author.objects.all()
>>> res = AuthorModelSerialize(instance=a, many=True)
>>> res
AuthorModelSerialize(instance=<QuerySet [<Author: 張三>, <Author: 李四>, <Author: 王五>, <Author: 龍傲天>]>, many=True):aid = IntegerField(read_only=True) name = CharField(label='姓名', read_only=True) address = CharField(label='地址', max_length=63) books_set = NestedSerializer(many=True, read_only=True): bid = IntegerField(read_only=True) name = CharField(label='書名', max_length=32) description = CharField(label='書籍描述', max_length=128) author = PrimaryKeyRelatedField(queryset=Author.objects.all())

extra_kwargs

由於ModelSerializer直接用model的欄位型別,肯定不能很好的做好條件約束
因此DRF提供了extra_kwargs屬性, 並且錯誤資訊也通過該引數進行自定製

class AuthorModelSerialize(serializers.ModelSerializer):
    class Meta:
        model = Author  # 指定使用Author這個模型
        # 指定欄位,注意:books_set
        fields = ("aid", "name", "phone", "address", "books_set")
        extra_kwargs = {
            "phone": {
                "min_length": 11,
                "max_length": 11
            }
        }

像普通Serialize那樣編寫

我們可以自定義一些額外的欄位或不用extra_kwargs屬性而直接model的欄位進行定義:

class AuthorModelSerialize(serializers.ModelSerializer):
    phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
	# write_only只反序列化,不入庫
    note = serializers.CharField(max_length=128, label="額外的欄位,不入庫", write_only=True)    

    class Meta:
        model = Author  # 指定使用Author這個模型
        # 指定欄位,注意:books_set
        fields = ("aid", "name", "address", "books_set")

指定外來鍵返回型別

外來鍵預設返回主鍵,但通過serializer_related_field屬性指定:

class AuthorModelSerialize(serializers.ModelSerializer):
    serializer_related_field = serializers.StringRelatedField        
	# 返回 __str__

    class Meta:
        model = Author
        fields = ("aid", "name", "address", "books_set")

驗證資料

這部分內容也是和Model一樣

驗證一個欄位

form一樣,只要在序列化器重定義一個validate_xxx方法(xxx指的是欄位名)即可驗證單個欄位,如下面的例子:

class AuthorSerialize(serializers.Serializer):
    aid = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32, label="姓名")
    phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
    address = serializers.CharField(max_length=63, label="地址")
    books_set = serializers.StringRelatedField(read_only=True, many=True)

    def validate_name(self, value:str):
        # 1. 判讀
        if not value.startswith("lcz"):
            # 丟擲ValidationError異常
            raise serializers.ValidationError("必須以lcz開頭", "startError")
        # 2.返回結果
        return value

ValidationError是一個異常,有兩個引數detailcode,前者接收異常的描述資訊,後者接收異常名(預設為"invalid"
該方法呼叫is_valid時自動執行
記得返回value

驗證一下:

>>> data = {
...     "name": "xxx",
...     "phone": "12345678910",
...     "note": "test",
...     "address": "廣東省廣州市xxxxx1",
... }
>>> result = AuthorSerialize(data=data)
>>> result.is_valid()
False
>>> result.errors
{'name': [ErrorDetail(string='必須以lcz開頭', code='startError')]}

驗證多個欄位

需要重寫validate方法:

class AuthorSerialize(serializers.Serializer):
    aid = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32, label="姓名")
    phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼")
    address = serializers.CharField(max_length=63, label="地址")
    books_set = serializers.StringRelatedField(read_only=True, many=True)

    def validate(self, attrs):
        """
        :param attrs: 外界傳入的需要校驗的字典
        :return: attrs
        """
        # 1. 判斷name和address(隨便舉個例子)
        if attrs["name"].startswith("lcz") and attrs.startswith("廣東省"):
            # 2.返回結果
            return attrs
        # 否則丟擲異常
        raise serializers.ValidationError("只有lcz開頭且地址是廣東省的才能通過驗證")

自定義驗證器

分兩步:

  1. 定義
    使用驗證器時,傳入的資料已經轉換為python的資料型別
    def check_bpub_date(date):
    	if date.year < 2015:
    		raise serializers.ValidationError("日期不能小於2015年")
    
    	return date
    
  2. 使用
    通過validators引數指定
    def check_pub_date(date):
    	if date.year < 2015:
    		raise serializers.ValidationError("日期不能小於2015年")
    	return date
    
    
    class BookSerialize(serializers.Serializer):
    	pub_date = serializers.DateTimeField(validators=[check_pub_date]
    
    

注:ModelSerialize通過extra_kwargs屬性指定

自定義錯誤資訊

  1. 內建錯誤通過error_messages引數指定
  2. 手動丟擲的異常,通過異常資訊指定

錯誤資訊是可以用一個函式專門處理的,詳情請看: 異常處理

如:

from rest_framework import serializers


class AuthorSerialize(serializers.Serializer):
    aid = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32, label="姓名")
    phone = serializers.CharField(min_length=11, max_length=11, label="手機號碼",
                                  error_messages={"min_length": "號碼太短了", "max_length": "號碼太長了"})
    address = serializers.CharField(max_length=63, label="地址")
    books_set = serializers.StringRelatedField(read_only=True, many=True)

和使用ValidationError的值定義為錯誤資訊:

def check_bpub_date(date):
    if date.year < 2015:
        raise serializers.ValidationError("日期不能小於2015年")
    return date

檢視

Django REST framework的檢視種類比序列化器還多,從一級檢視到檢視集,每一個都是在之前的檢視基礎上進行了封裝,至於用哪個,全憑自己的喜好了。
詳情見下圖:
DRF檢視

Django REST framework的檢視都是用CBV的寫法

由於REST API的url是固定的,所以可以把檢視分為 列表檢視和詳情檢視,可能有點抽象,舉個例子:

  • 列表檢視:GET /books(獲取所有的書籍);POST /books (建立書籍)
  • 詳情檢視:GET /books/{id}(獲取單個書籍 );PUT /books/{id}(修改書籍);DELETE /books/{id} (刪除書籍)

可以感受出來了吧,對資料做增刪改查需要兩個路由5個函式, 因此我們可以將由於檢視類分為 列表檢視 和 詳情檢視兩個檢視類,不過我們可以利用 檢視集 將檢視類變為一個

request和responses

django中任何檢視必然至少接收一個request,返回一個responseDjango REST framework也不例外。

request

DRFRequest類擴充套件了django預設的HttpRequest,它主要有兩個屬性:

  1. Request.data
    得到請求體的資料(表單資料和JSON資料都行)
  2. Request.query_params
    得到查詢資料(任何HTTP方法型別可能包括查詢引數,而不僅僅是GET請求

除此外還有關於認證等屬性,由於與django原來的request類似, 故這裡不展開

responses

Response的簽名:Response(data, status=None, template_name=None, headers=None, content_type=None)
一般我們只需要用到前兩個引數即可:

  • data: response的數列化資料
  • status: response的狀態碼。預設是200
  • template_name: HTMLRenderer 選擇要使用的模板名稱
  • headers: `響應頭,是一個字典
  • content_type: response的內容型別。通常由渲染器自行設定

常用狀態碼

你可以在rest_framework.status中找到所有狀態碼

程式碼變數 對應狀態碼 說明
HTTP_200_OK 200 請求成功
HTTP_201_CREATED 201 該請求已成功,並因此建立了一個新的資源, 一般為POSTPUT成功後返回
HTTP_204_NO_CONTENT 204 伺服器成功處理了請求,但不需要返回任何實體內容, 一般為DELETE成功後返回
HTTP_400_BAD_REQUEST 400 語義有誤,當前請求無法被伺服器理解 或 請求引數有誤
HTTP_401_UNAUTHORIZED 401 當前請求需要使用者驗證
HTTP_403_FORBIDDEN 403 伺服器已經理解請求,但是拒絕執行它
HTTP_404_NOT_FOUND 404 請求失敗,請求所希望得到的資源未被在伺服器上發現
HTTP_500_INTERNAL_SERVER_ERROR 500 伺服器遇到了不知道如何處理的情況
HTTP_502_BAD_GATEWAY 502 伺服器作為閘道器需要得到一個處理這個請求的響應,但是得到一個錯誤的響應
HTTP_503_SERVICE_UNAVAILABLE 503 伺服器沒有準備好處理請求。 常見原因是伺服器因維護或過載而停機

一級檢視APIView

rest_framework.views.APIViewdjango.views.generic.base.View的子類,用起來和普通的傳統的繼承View的寫法差不多

列表檢視

列表檢視的寫法:

  1. 定義一個類,繼承APIView
  2. 定義方法(get, post
  3. request中獲取資料
  4. 利用 序列化器 序列化資料或檢驗資料入庫
  5. 返回(除非丟擲異常,否則返回的都是data的資料)
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from rest_test.serializers import AuthorModelSerialize
from rest_test.models import Author


class AuthorListView(APIView):
    def get(self, request):
        """
        獲取全部資料
        :param request: 
        :return: 
        """
        authors = Author.objects.all()
		# instance指定要序列化的物件
		# 多個資料用many=True
        serialize = AuthorModelSerialize(instance=authors, many=True)
        return Response(serialize.data, status=status.HTTP_200_OK)

    def post(self, request):
        """
        建立資料
        :param request: 
        :return: 
        """
        serialize = AuthorModelSerialize(data=request.data)
        
        # raise_exception=True,假如報錯,會自動處理
        # 返回狀態碼:400,且資料的形式:{"phone": ["Ensure this field has no more than 11 characters."]}
        
        serialize.is_valid(raise_exception=True)
        serialize.save()
        return Response(serialize.data, status=status.HTTP_201_CREATED)

別忘了寫路由規則:

from django.contrib import admin
from django.urls import path
from rest_test import views

urlpatterns = [
    path('admin/', admin.site.urls),
	# 同樣呼叫.as_view方法
    path("authors/", views.AuthorListView.as_view())
]

詳情檢視

在原來的基礎上多了主鍵,用來操作單個資料:

  1. 同樣繼承APIView
  2. 定義get put delete四個方法
  3. 使用get_object_or_404取得要操作的物件(不用我們處理異常)
  4. 返回資料
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from rest_test.serializers import AuthorModelSerialize
from rest_test.models import Author
from django.shortcuts import get_object_or_404


class AuthorDetailView(APIView):
    def get(self, request, pk):
        # 這是django的內建方法,不會的可以查一下
        author = get_object_or_404(Author, pk=pk)

        serialize = AuthorModelSerialize(instance=author)
        return Response(serialize.data, status=status.HTTP_200_OK)

    def put(self, request, pk):
        author = get_object_or_404(Author, pk=pk)
		
        # 相容patch方法的部分更新,一般我們都不會在定義個patch方法
        serialize = AuthorModelSerialize(instance=author, data=request.data, partial=True)
        serialize.is_valid(raise_exception=True)
        serialize.save()
        return Response(serialize.data, status.HTTP_201_CREATED)

    def delete(self, request, pk):
        author = get_object_or_404(Author, pk=pk)
        author.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

路由:

from django.contrib import admin
from django.urls import path
from rest_test import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path("authors/", views.AuthorListView.as_view()),
    path("authors/<int:pk>/", views.AuthorDetailView.as_view())
]

二級檢視GenericAPIView

一級檢視已經可以很好地幫我們完成任務了,但是上面地程式碼明顯有冗餘部分:
如,詳情檢視呼叫3次get_object_or_404,所以Django REST framework有更好地方法定義。

介紹

GenericAPIViewAPIView的子類,它將我們用到的序列化器和模型已經獲取model物件的方法進行了整合,即新增了常用的行為和屬性。雖然我們也可以做到這樣,但是它已經做好了,那麼我們只需要瞭解規則就可以直接使用了。
GenericAPIView有4個重要屬性和3個重要方法:

  • 屬性
    1. queryset: 通用的資料集,通常是XXX.objects.all()
    2. serializer_class: 通用的序列化器
    3. lookup_field: 主鍵的名稱,預設是pk,主鍵,請注意與lookup_url_kwarg區分
    4. lookup_url_kwargurl引數中的標識,如:/books/1 若:lookup_url_kwargbooks_id,那麼1這個值就要用books_id接收
  • 行為(方法)
    1. get_queryset: 獲取queryset的資料集
    2. get_serializer: 獲取serializer_class序列化器物件
    3. get_object: 根據lookup_field獲取單個物件

列表檢視

需要用到queryset()serializer_class()get_queryset()get_serializer()

from rest_framework.generics import GenericAPIView
from rest_framework import status
from rest_framework.response import Response
from rest_test.serializers import AuthorModelSerialize
from rest_test.models import Author


class AuthorListView(GenericAPIView):
   queryset = Author.objects.all()
   serializer_class = AuthorModelSerialize

   def get(self, request):
       serialize = self.get_serializer(instance=self.get_queryset(), many=True)
       return Response(serialize.data, status=status.HTTP_200_OK)

   def post(self, request):
       data = request.data
       serialize = self.get_serializer(data=data)
       serialize.is_valid(raise_exception=True)
       serialize.save()
       return Response(serialize.data, status=status.HTTP_201_CREATED)

路由如一級檢視, 保持不變

詳情檢視

4個屬性和3個方法,都可以用,隨便發揮。

from rest_framework.generics import GenericAPIView
from rest_framework import status
from rest_framework.response import Response
from rest_test.serializers import AuthorModelSerialize
from rest_test.models import Author



class AuthorDetailView(GenericAPIView):
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerialize
    lookup_url_kwarg = "author_id"

    def get(self, request, author_id):
        # 相當於get_object_or_404
        instance = self.get_object()
        serialize = self.get_serializer(instance=instance)
        return Response(serialize.data, status=status.HTTP_200_OK)

    def put(self, request, author_id):
        data = request.data
        instance = self.get_object()
        serialize = self.get_serializer(instance=instance, data=data, partial=True)
        serialize.is_valid(raise_exception=True)
        serialize.save()
        return Response(serialize.data, status.HTTP_201_CREATED)

    def delete(self, request, author_id):
        instance = self.get_object()
        instance.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

由於指定了lookup_url_kwarg,路由改變

from django.contrib import admin
from django.urls import path
from rest_test import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path("authors/", views.AuthorListView.as_view()),
    path("authors/<int:author_id>/", views.AuthorDetailView.as_view())
]

與Mixin類結合

二級檢視的另一特點就是可以與Minx類相結合。

python的Mixin類通常只是為子類類提供某些功能,而不自己例項化的類

Django REST framework有這幾個Mixin類可以為我們提供通用的列表檢視和詳情檢視解決方法:

類名 方法 作用
ListModelMixin list 查詢所有的資料
CreateModelMixin create 建立單個物件
RetrieveModelMixin retrieve 獲取單個物件
UpdateModelMixin update 更新單個物件
DestroyModelMixin destroy 刪除單個物件

這些類都在rest_framework.mixins

由上表可知:

  • 檢視列表: ListModelMixin + CreateModelMixin
  • 詳情檢視: RetrieveModelMixin + UpdateModelMixin + DestroyModelMixin

除此之外,你還可以根據需求任意組合

使用起來也特別簡單

  1. 繼承GenericAPIView對應的Mixin
  2. 定義方法(get, post, put, delete
  3. 返回Mixin類中的處理方法(記得傳入request等引數)

例子:

  • 列表檢視
    from rest_framework.generics import GenericAPIView
    from rest_framework.mixins import ListModelMixin, CreateModelMixin
    from rest_test.serializers import AuthorModelSerialize
    from rest_test.models import Author
    
    
    class AuthorListView(GenericAPIView, ListModelMixin, CreateModelMixin):
        queryset = Author.objects.all()
        serializer_class = AuthorModelSerialize
    
        def get(self, request):
            return self.list(request=request)
    
        def post(self, request):
            return self.create(request=request)
    
  • 詳情檢視
    
    from rest_framework.generics import GenericAPIView
    from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin
    from rest_test.serializers import AuthorModelSerialize
    from rest_test.models import Author
    
    
    class AuthorDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        queryset = Author.objects.all()
        serializer_class = AuthorModelSerialize
        lookup_url_kwarg = "author_id"
    
        def get(self, request, author_id):
            return self.retrieve(request, author_id)
    
        def put(self, request, author_id):
            return self.update(request, author_id)
    
        def delete(self, request, author_id):
            return self.destroy(request, author_id)
    

雖然Mixin類已經寫好了處理邏輯,但是它是通過呼叫其它方法的形式實現的,所以我們可以重寫部分方法,可以使Mixin類更加靈活。

如:DestroyModelMixin類,這是原始碼:

class DestroyModelMixin:
    """
    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()

重寫perform_destroy方法就可以實現自訂製了,具體有哪些方法可以檢視原始碼,這裡不展開了

三級檢視xxxAPIView

Django REST framework的檢視簡直跟套娃一樣,一層套一層

所謂的三級檢視,就是將GenericAPIView和一個Mixin類繼承到一個子類中,實現列表檢視和詳情檢視的某個方法

有一說一,如果沒有大量自定義的行為, 確實可以使用三級檢視快速構建一個專案

常見的三級檢視:

類名稱 父類 提供方法 作用
CreateAPIView GenericAPIView CreateModelMixin post 建立單個物件
ListAPIView GenericAPIView ListModelMixin get 查詢所有的資料
RetrieveAPIView GenericAPIView RetrieveModelMixin get 獲取單個物件
DestroyAPIView GenericAPIView DestroyModelMixin delete 刪除單個物件
UpdateAPIView GenericAPIView UpdateModelMixin put 更新單個物件

這些類在rest_framework.generics

使用:

from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView
from rest_test.serializers import AuthorModelSerialize
from rest_test.models import Author


class AuthorListView(ListAPIView, CreateAPIView):
    # 列表檢視
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerialize


class AuthorDetailView(RetrieveAPIView, UpdateAPIView, DestroyAPIView):
    # 詳情檢視
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerialize
    lookup_url_kwarg = "author_id"

檢視集

檢視集就是將整個列表檢視和詳情檢視變成一個類,然後我們只需要指定queryset serializer_class lookup_field lookup_url_kwarg即可。

特點:

  1. 可以將一組相關的操作,放在一個類中進行完成
  2. 處理方法變化了:
    • get全部 --> list
    • post --> create
    • get單個 --> retrieve
    • put --> update
    • delete --> destroy

檢視集很重要的一點是可以在urls中做路由對映

常見的檢視集:

類名稱 父類 作用
ViewSet APIView ViewSetMixin 可以做路由對映
GenericViewSet GenericAPIView ViewSetMixin 可以使用4個屬性 3個方法, 其他同上
ModelViewSet GenericAPIView和5個mixin 整合所有的增刪改查功能, 其他同上
ReadOnlyModelViewSet GenericAPIView RetrieveModelMixin ListModelMixin 可以做路由對映, 整合單個和所有資料的功能, 可以使用4個屬性 3個方法

這些類在rest_framework.viewsets

使用檢視集

下面介紹四中檢視集類的使用方式

  1. ViewSet
    檢視,用一個類即可定義全部方法:

    from rest_framework.viewsets import ViewSet
    from django.shortcuts import get_object_or_404
    from rest_framework import status
    from rest_framework.response import Response
    from rest_test.serializers import AuthorModelSerialize
    from rest_test.models import Author
    
    class AuthorViewSet(ViewSet):
       def list(self, request):
           authors = Author.objects.all()
           serialize = AuthorModelSerialize(instance=authors, many=True)
           return Response(serialize.data, status=status.HTTP_200_OK)
    
       def create(self, request):
           data = request.data
           serialize = AuthorModelSerialize(data=data)
           serialize.is_valid(raise_exception=True)
           serialize.save()
           return Response(serialize.data, status=status.HTTP_201_CREATED)
    
       def retrieve(self, request, author_id):
           author = get_object_or_404(Author, pk=author_id)
           serialize = AuthorModelSerialize(instance=author)
           return Response(serialize.data, status=status.HTTP_200_OK)
    
       def update(self, request, author_id):
           data = request.data
           author = get_object_or_404(Author, pk=author_id)
           serialize = AuthorModelSerialize(instance=author, data=data, partial=True)
           serialize.is_valid(raise_exception=True)
           serialize.save()
           return Response(serialize.data, status=status.HTTP_201_CREATED)
    
       def destroy(self, request, author_id):
           author = get_object_or_404(Author, pk=author_id)
           author.delete()
           return Response(status=status.HTTP_204_NO_CONTENT)
    
    

    做路由對映,由於列表檢視和詳情檢視的url不一樣,所以需要做兩次對映:

    from django.contrib import admin
    from django.urls import path
    from rest_test import views
    
    urlpatterns = [
       path('admin/', admin.site.urls),
       path("authors/", views.AuthorViewSet.as_view({"get": "list", "post": "create"})),
       path("authors/<int:author_id>/",
            views.AuthorViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}))
    ]
    

    路由對映的意思就是,這個url的哪個請求方式,去哪個方法處理
    路由中的as_view方法: 引數是一個字典, 鍵為請求方式, 值為對應的處理函式
    如: path("authors/", views.AuthorViewSet.as_view({"get": "list", "post": "create"}))
    意為:authors/get方法去list函式那裡處理,而post方法去create函式那裡處理

  2. GenericViewSet

    下面3個類都繼承了GenericAPIView,操作起來更簡單

    from rest_framework import status
    from rest_framework.response import Response
    from rest_test.serializers import AuthorModelSerialize
    from rest_test.models import Author
    from rest_framework.viewsets import GenericViewSet
    
    class AuthorViewSet(GenericViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorModelSerialize
        lookup_url_kwarg = "author_id"
    
        def list(self, request):
            authors = self.get_queryset()
            serialize = self.get_serializer(instance=authors, many=True)
            return Response(serialize.data, status=status.HTTP_200_OK)
    
        def create(self, request):
            data = request.data
            serialize = self.get_serializer(data=data)
            serialize.is_valid(raise_exception=True)
            serialize.save()
            return Response(serialize.data, status=status.HTTP_201_CREATED)
    
        def retrieve(self, request, author_id):
            author = self.get_object()
            serialize = self.get_serializer(instance=author)
            return Response(serialize.data, status=status.HTTP_200_OK)
    
        def update(self, request, author_id):
            data = request.data
            author = self.get_object()
            serialize = self.get_serializer(instance=author, data=data, partial=True)
            serialize.is_valid(raise_exception=True)
            serialize.save()
            return Response(serialize.data, status=status.HTTP_201_CREATED)
    
        def destroy(self, request, author_id):
            author = self.get_object()
            author.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
    

    路由使用ViewSet的程式碼

  3. ReadOnlyModelViewSet
    只讀的檢視集

    from rest_framework.viewsets import ReadOnlyModelViewSet
    from rest_test.serializers import AuthorModelSerialize
    from rest_test.models import Author
    
    
    class AuthorReadOnlyViewSet(ReadOnlyModelViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorModelSerialize
        lookup_url_kwarg = "author_id"
    
    

    改變路由

    from django.contrib import admin
    from django.urls import path
    from rest_test import views
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path("authors/", views.AuthorReadOnlyViewSet.as_view({"get": "list"})),
        path("authors/<int:author_id>/", views.AuthorReadOnlyViewSet.as_view({"get": "retrieve"}))
    ]
    
  4. ModelViewSet
    模型字符集,把列表檢視和詳情檢視已經整合在一起了
    注意:部分更新的函式是partial_update

    from rest_framework.viewsets import ModelViewSet
    from rest_test.serializers import AuthorModelSerialize
    from rest_test.models import Author
    
    
    class AuthorViewSet(ModelViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorModelSerialize
        lookup_url_kwarg = "author_id"
    
    

    改變路由

    from django.contrib import admin
    from django.urls import path
    from rest_test import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path("authors/", views.AuthorViewSet.as_view({"get": "list", "post": "create"})),
        path("authors/<int:author_id>/",
             views.AuthorViewSet.as_view(
                 {"get": "retrieve", "put": "update", "delete": "destroy", "patch": "partial_update"}))
    ]
    
    

自定義額外方法

我們還可以定義一些方法,讓檢視集可以處理更多請求。
如: 獲取id大於4的作者:

  1. 定義檢視集
    from rest_framework.viewsets import ReadOnlyModelViewSet
    from rest_test.serializers import AuthorModelSerialize
    from rest_test.models import Author
    
    
    # 為了看起來更簡潔,使用ReadOnlyModelViewSet
    class AuthorReadOnlyViewSet(ReadOnlyModelViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorModelSerialize
        lookup_url_kwarg = "author_id"
    
        def gt_id(self, request):
            queryset = self.filter_queryset(self.get_queryset())
            authors = queryset.filter(pk__gt=4)
            serialize = self.get_serializer(instance=authors, many=True)
            return Response(serialize.data, status=status.HTTP_200_OK)
    
    
  2. 寫路由對映關係
    from django.contrib import admin
    from django.urls import path
    from rest_test import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path("authors/", views.AuthorReadOnlyViewSet.as_view({"get": "list"})),
        path("authors/<int:author_id>/", views.AuthorReadOnlyViewSet.as_view({"get": "retrieve"})),
        # 注意這裡 !!!!!!!! get請求方式,找gt_id方法
        path("authors/gt4/", views.AuthorReadOnlyViewSet.as_view({"get": "gt_id"}))
    ]
    

注意:假如使用Django REST framework的方式定義路由的話,不能像上面那樣寫,要這樣

from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_test.serializers import AuthorModelSerialize
from rest_test.models import Author
from rest_framework.decorators import action


class AuthorReadOnlyViewSet(ReadOnlyModelViewSet):
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerialize
    lookup_url_kwarg = "author_id"
    
    # 生成路由規則: 字首/方法名/  detail=True時,需要主鍵的值且url為:字首/{pk}/方法名/
    @action(methods=['GET'], detail=False)  
    def gt_id(self, request):
        queryset = self.filter_queryset(self.get_queryset())
        authors = queryset.filter(pk__gt=7)
        serialize = self.get_serializer(instance=authors, many=True)
        return Response(serialize.data, status=status.HTTP_200_OK)

至於路由如何寫,見下文的路由部分

總結一下

捋一下思路

  1. 一級檢視只是繼承了View,用法和原生Django十分相似,幾乎所有方法都要我們寫。
  2. 二級檢視將序列化器和模型資料定義在了類屬性中,並且提供了獲取單個模型的方法(get_object),簡化了部分操作。最重要的是它可以與Mixin類結合,能夠減少的部分工作,但是仍然需要自定義方法,然後再方法中呼叫Mixin類的方法。
  3. 三級檢視則是父類直接提供方法,我們繼承該類就可以使用了。
  4. 這三種檢視定義方法,有一個明顯的缺點 (可能吧) : 我們必須要定義多個檢視類(列表檢視和詳細檢視,要新增額外方法的話,可能還要增加檢視數量)
  5. 檢視集可以在路由中進行路由對映,因此可以把所有處理方法都定義在同一個類裡面,而且它也提供了從簡單到複雜的檢視集類,方便我們的使用
  6. 至於在實際開發過程中,要用哪個類,全憑自己選擇吧,最多多寫幾段程式碼而已

路由

SimpleRouter

一般使用:

  1. router = SimpleRouter()
  2. 呼叫register方法
  3. urlpatterns += router.urls
from django.contrib import admin
from django.urls import path
from rest_test import views
from rest_framework.routers import DefaultRouter, SimpleRouter

urlpatterns = [
    path('admin/', admin.site.urls),
]

router = SimpleRouter()
router.register("authors", views.AuthorReadOnlyViewSet, basename="authors")

# 是authors不是authors/

urlpatterns += router.urls
print(urlpatterns)
"""
[<URLResolver <URLPattern list> (admin:admin) 'admin/'>, 
<URLPattern '^authors/$' [name='authors-list']>, 
<URLPattern '^authors/(?P<author_id>[^/.]+)/$' [name='authors-detail']>,
<URLPattern '^authors/$' [name='authors-list']>, 
<URLPattern '^authors/(?P<author_id>[^/.]+)/$' [name='authors-detail']>]

"""

DefaultRouter

該路由可以在SimpleRouterd的基礎上生成可選的.json樣式格式字尾的路由和根路由。

from django.contrib import admin
from django.urls import path
from rest_test import views
from rest_framework.routers import DefaultRouter, SimpleRouter

urlpatterns = [
    path('admin/', admin.site.urls),
]

router = DefaultRouter()
router.register("authors", views.AuthorReadOnlyViewSet, basename="authors")

# 是authors不是authors/

urlpatterns += router.urls
print(urlpatterns)
"""
[<URLResolver <URLPattern list> (admin:admin) 'admin/'>,
<URLPattern '^authors/$' [name='authors-list']>, 
<URLPattern '^authors\.(?P<format>[a-z0-9]+)/?$' [name='authors-list']>,
<URLPattern '^authors/gt_id/$' [name='authors-gt-id']>, 
<URLPattern '^authors/gt_id\.(?P<format>[a-z0-9]+)/?$' [name='authors-gt-id']>, 
<URLPattern '^authors/(?P<author_id>[^/.]+)/$' [name='authors-detail']>, 
<URLPattern '^authors/(?P<author_id>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='authors-detail']>, 
<URLPattern '^authors/$' [name='authors-list']>,
<URLPattern '^authors\.(?P<format>[a-z0-9]+)/?$' [name='authors-list']>, 
<URLPattern '^authors/(?P<author_id>[^/.]+)/$' [name='authors-detail']>, 
<URLPattern '^authors/(?P<author_id>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='authors-detail']>, 
<URLPattern '^$' [name='api-root']>, <URLPattern '^\.(?P<format>[a-z0-9]+)/?$' [name='api-root']>]
"""

限制訪問

這部分為限制訪問的內容,由認證、許可權和限流三個部分組成。

Authentication

此部分講的是認證內容,即如何確定你是你??

有兩種配置方式:

  1. settings中配置全域性的
    如:
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.BasicAuthentication',
            'rest_framework.authentication.SessionAuthentication',
        )
    }
    
  2. 在檢視中配置本檢視的
    from rest_framework.authentication import SessionAuthentication, BasicAuthentication
    
    
    class BookInfoModelViewSet(ModelViewSet):
        authentication_classes = (SessionAuthentication, BasicAuthentication)
        pass
    

一般來說我們常用有這幾種驗證方式:

  1. BasicAuthentication
    基於HTTP的認證方式,要求驗證的時候會彈出一個框來,讓你輸入賬號和密碼,一般只在開發測試時使用
  2. SessionAuthentication
    基於Session的認證,這裡應該用的時django自帶的AUTH元件
  3. TokenAuthentication
    基於Token的HTTP認證方案,即生成一個令牌,作為標識,請求頭帶上Authorization: Token token的值即可
  4. JSON Web Token Authentication
    基於jwt的認證方式

注:可以多個認證方式同時使用

下面將說如何使用

BasicAuthentication

全域性配置:


REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        'rest_framework.authentication.BasicAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        # 'rest_framework.permissions.IsAuthenticated', # 普通使用者
        # 'rest_framework.permissions.AllowAny',         # 所有使用者
        'rest_framework.permissions.IsAdminUser',          # 管理員戶
    )
}

DEFAULT_PERMISSION_CLASSES是許可權,後面會講到,可以先看看註釋

假如我們要求有一定許可權才能登入的話,那麼它就會彈出如下面這樣的對話方塊:
對話方塊
這裡登入用的是django的使用者賬號(就是登入django admin的那種賬號),是可以擴充套件的

SessionAuthentication

使用SessionAuthentication首先就是需要注意CSRF的問題

全域性配置(只換來認證器):

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        # 'rest_framework.permissions.IsAuthenticated', #普通使用者
        # 'rest_framework.permissions.AllowAny', #所有使用者
        'rest_framework.permissions.IsAdminUser',  # 管理員戶
    )
}

登入頁面需要我們自己寫,很簡單,可以看我這篇文章Django auth。但這裡我們是學習DRF,那麼就用一個REST API介面登入:
如果我們將登入看作是建立一個線上使用者,那麼logout就是刪除一個線上使用者了。

# views.py
from rest_framework.views import APIView
from rest_framework.permissions import AllowAny
from django.contrib.auth import authenticate, login, logout


class SessionView(APIView):
    permission_classes = (AllowAny, )
    # 必須寫AllowAny(即每個人都可以訪問),不然連登入的許可權都沒有!!

    def post(self, request):
        username = request.data.get("username")
        password = request.data.get("password")
        user = authenticate(request, username=username, password=password)

        if user is not None:
            # 登入,寫入session資訊
            login(request, user)
            return Response({"detail": "success!"}, status=status.HTTP_201_CREATED)
        else:
            return Response({"detail": "password or username error!"}, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request):
        logout(request)
        return Response({"detail": "logout success!!"}, status=status.HTTP_204_NO_CONTENT)


然後再新增路由

urlpatterns = [
    path('admin/', admin.site.urls),
    path("session/", views.SessionView.as_view())
]

訪問session/頁面,方式POST請求登入即可。

TokenAuthentication

  1. 配置settings:
    INSTALLED_APPS = [
     "rest_framework.authtoken"
    ]
    # ...
    
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": (
            'rest_framework.authentication.TokenAuthentication',
        ),
        'DEFAULT_PERMISSION_CLASSES': (
            # 'rest_framework.permissions.IsAuthenticated', #普通使用者
            # 'rest_framework.permissions.AllowAny', #所有使用者
            'rest_framework.permissions.IsAdminUser',  # 管理員使用者
        )
    }
    
    
  2. 執行遷移命令,這時會生成一個authtoken_token
    $python manage.py migrate
    
  3. 編寫登入檢視
    from rest_framework.views import APIView
    from rest_framework.permission import AllowAny
    from django.contrib.auth import authenticate
    from rest_framework.authtoken.models import Token
    
    
    class TokenView(APIView):
        permission_classes = (AllowAny,)
    
        def post(self, request):
            username = request.data.get("username")
            password = request.data.get("password")
            user = authenticate(request, username=username, password=password)
    
            if user is not None:
                # 刪除原有的Token
                old_token = Token.objects.filter(user=user)
                old_token.delete()
                # 建立新的Token
                token = Token.objects.create(user=user)
                return Response({"code": 0,
                                 "msg": "login success!",
                                 "username": user.username,
                                 "token": token.key}, status=status.HTTP_201_CREATED)
            else:
                return Response({"detail": "password or username error!"}, status=status.HTTP_400_BAD_REQUEST)
    
        def delete(self, request):
            if request.user.is_authenticated:
                user = request.user
                old_token = Token.objects.filter(user=user)
                old_token.delete()
            return Response({"detail": "logout success!!"}, status=status.HTTP_204_NO_CONTENT)
    
    
  4. 配置路由
    from django.contrib import admin
    from django.urls import path
    from rest_test import views
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path("token/", views.TokenView.as_view())
    ]
    
  5. 將使用者名稱和密碼利用POST方式提交給token後就會返回token
    獲得token後,將token放在請求頭即可。
    如:獲取token為:323ce6f331c7aa65836ad48169037f001ce7e18f
    那麼請求頭就為:Authorization: Token 323ce6f331c7aa65836ad48169037f001ce7e18f

JSON Web Token Authentication

  1. 安裝djangorestframework-simplejwt,這是目前官網推薦的python jwt庫

    $pip install djangorestframework-simplejwt
    
  2. 配置驗證器為:

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": (
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        ),
        'DEFAULT_PERMISSION_CLASSES': (
            'rest_framework.permissions.IsAuthenticated',  # 普通使用者
            # 'rest_framework.permissions.AllowAny',       # 所有使用者
            # 'rest_framework.permissions.IsAdminUser',    # 管理員使用者
        )
    }
    
    
  3. 配置jwt驗證器

    # settings.py
    
    import datetime
    
    
    SIMPLE_JWT = {
    	# token有效時長
    	'ACCESS_TOKEN_LIFETIME': datetime.timedelta(minutes=30),
    	# token重新整理後的有效時間
    	'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1)
    }
    
  4. 定義路由規則
    由於simplejwt已經寫好了檢視,我們只需要直接拿來用即可:

    from django.contrib import admin
    from django.urls import path
    
    from rest_framework_simplejwt.views import (
    	TokenObtainPairView,
    	TokenRefreshView,
    	TokenVerifyView
    )
    
    
    urlpatterns = [
    	path('admin/', admin.site.urls),
    
    	# 獲取Token的介面
    	path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    	# 重新整理Token有效期的介面
    	path('api/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    	# 驗證Token的有效性
    	path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
    ]
    
  5. 將使用者名稱和密碼提交到POST api/token/
    返回的資訊包括refreshaccess兩個欄位。
    其中refresh是用於重新整理token的(每個token都是有時間限制的,過了時間就失效了)
    access是用於後續的請求時驗證身份的。

  6. access的值放入請求頭中:
    如:獲取的accesseyJ0eiJIUiJ9.eyJ0b2tlzZXJjF9.n5FTesf9i4X3t7X0JEQ
    那麼,請請求頭就是:Authorization: Bearer eyJ0eiJIUiJ9.eyJ0b2tlzZXJjF9.n5FTesf9i4X3t7X0JEQ

上面的路由:
api/token/verify/ 可以檢驗token,需要POST access的值
api/refresh/ 可以重新整理token,需要POST refresh的值

關於更多simple-jwt的資訊,請檢視官方文件:Simple JWT

permission

許可權可以是能不能讓使用者訪問到頁面的關鍵,不過你得先通過了認證。

指定許可權

通用可以時全域性和檢視的

  • 全域性(在settings中定義):
    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': (
            'rest_framework.permissions.IsAuthenticated',  # 普通使用者
        )
    }
    
  • 檢視(在檢視中定義):
    from rest_framework.permissions import AllowAny
    from rest_framework.viewsets import ModelViewSet
    
    
    class BookInfoModelViewSet(ModelViewSet):
        permission_classes = (AllowAny,)
        # ...
    
    

常用內建許可權

  • rest_framework.permissions.AllowAny
    不管誰都可以

  • rest_framework.permissions.IsAuthenticatedOrReadOnly
    允許經過身份驗證的使用者執行任何請求,匿名使用者可以僅僅可以讀取(GET

  • rest_framework.permissions.IsAuthenticated
    僅允許經過身份驗證的使用者執行任何請求(即已經登入的使用者才能訪問)

  • rest_framework.permissions.IsAdminUser
    僅允許request.user.is_staffTrue的使用者執行任何請求(說明這是一個管理員賬號)

  • rest_framework.permissions.DjangoModelPermissions
    僅允許使用者通過身份驗證並分配了相關模型許可權的情況下的使用者執行任何請求

    特別注意:此許可權只能應用於具有queryset屬性集的檢視

DjangoModelPermissions

由於內建的許可權(只有增刪改查)粒度不夠細,不足以應付複雜的需求,所有這裡演示一些使用我們自定義的許可權
關於django的許可權問題可以檢視此文:許可權
要使用自定義模型許可權,請繼承DjangoModelPermissions並設定perms_map屬性

DjangoModelPermissions的部分原始碼:

perms_map = {
    'GET': [],
    'OPTIONS': [],
    'HEAD': [],
    'POST': ['%(app_label)s.add_%(model_name)s'],
    'PUT': ['%(app_label)s.change_%(model_name)s'],
    'PATCH': ['%(app_label)s.change_%(model_name)s'],
    'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}

例子:

# views.py
class CustomPermClass(DjangoModelPermissions):
    # GET請求許可權, can_publish
    perms_map = {"GET": ['%(app_label)s.can_publish']}


class AuthorViewSet(ModelViewSet):
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerialize
    lookup_url_kwarg = "author_id"
    permission_classes = (CustomPermClass,)
	
	
# models.py
class Books(models.Model):
    bid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, verbose_name="書名")
    description = models.CharField(max_length=128, verbose_name="書籍描述")
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

    def __str__(self):
        return f"{self.author.name}: 《{self.name}》"

    class Meta:
        permissions = (("can_publish", "能夠出版"),)

另: DRF有與Django的標準物件許可權框架相關聯的類, 見: DjangoObjectPermissions

限流

即讓請求在一段時間內只能訪問多少次。

  • 全域性定義
    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.AnonRateThrottle', # 匿名使用者
            'rest_framework.throttling.UserRateThrottle'  # 認證使用者
        ],
        'DEFAULT_THROTTLE_RATES': {
            'anon': '2/minute',     # 匿名使用者每分鐘2次
            'user': '3/minute'        # 認證使用者每分鐘3次
        }
    }
    
  • 檢視中定義
    from rest_framework.throttlingimport AnonRateThrottle
    from rest_framework.viewsets import ModelViewSet
    class BookInfoModelViewSet(ModelViewSet):
        throttle_classes = (AnonRateThrottle, )
        # ...
    
  • 可選限流
    # settings.py
    
    
    REST_FRAMEWORK = {
    
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.ScopedRateThrottle',
        ],
        'DEFAULT_THROTTLE_RATES': {
            'downloads': '3/minute',    # downloads 自定義的
            'uploads': '5/minute'        # uploads 自定義的
        }
    }
    
    # views.py
    
    class TestView(APIView):
        throttle_scope = "uploads"
        def get(self,request):
            return Response("testing....")
    

篩選返回資料

分頁

使用預設的分頁器

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 2,
    
}
  • PAGE_SIZE指的是每頁多少條資料

其預設生成的資料是這樣的:

{
    "count": 9,
    "next": "http://127.0.0.1:8000/authors/?limit=2&offset=6",
    "previous": "http://127.0.0.1:8000/authors/?limit=2&offset=2",
    "results": [
        {
            "aid": 8,
            "name": "張三",
            "phone": "13888888888",
            "address": "廣東省廣州市xxxxx",
            "books_set": []
        },
        {
            "aid": 9,
            "name": "李四",
            "phone": "13888888888",
            "address": "廣東省廣州市xxxxx",
            "books_set": []
        }
    ]
}

http://127.0.0.1:8000/authors/?limit=2&offset=6中的limit指取多少條資料,offset指偏移量,即跳過多少條資料
預設情況下limit是沒有限制的,也就是說你指定1000000都行,為了限度這些行為,我們可以自定義一個分頁器

自定義一個分頁器

只需要繼承rest_framework.pagination.PageNumberPagination,裡面我們可以通過自定義類屬性的形式,自定義分頁器:
常用的屬性

  • page_size 頁面大小的數值
  • page_size_query_param 可以指定頁面大小
  • max_page_size 指示允許的最大請求頁面大小

還有些沒有列出來,可以檢視官方文件


#自定義分頁物件
class MyPageNumberPagination(PageNumberPagination):
    #1,預設的大小
    page_size = 3

    #2,前端可以指定頁面大小
    page_size_query_param = 'page_size'

    #3,頁面的最大大小
    max_page_size = 5


# 檢視集
class BookInfoModelViewSet(ModelViewSet):
    # ...
    pagination_class = MyPageNumberPagination
    #  連結可以是,?page=4 或者 ?page=4&page_size=100

排序

這個功能可以讓資料按照指定的欄位進行排序

from rest_framework.filters import OrderingFilter


class BookInfoModelViewSet(ModelViewSet):
    # ....
    # 區域性排序
    filter_backends = (OrderingFilter, ) 
    
    # 指定排序欄位
    ordering_fields = ['id', 'btitle','bread']
    
    # 查詢格式: ?ordering=-bread,id

過濾

過濾功能可以根據文件配置,進行過濾返回的資料

這不是Django REST framework的元件,需要我們安裝django-filter

  1. 安裝django-filter
    $pip install django-filter
    
  2. django-filter配置到INSTALLED_APPS
    INSTALLED_APPS = [
        # ...
        'django_filters',
    ]
    
  3. 配置filter
    全域性:
    REST_FRAMEWORK = {
        # ...
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
    }
    
    檢視:
    from django_filters.rest_framework import DjangoFilterBackend
    class BookInfoModelViewSet(ModelViewSet):
        # ....
      filter_backends = (DjangoFilterBackend, )
      filterset_fields = ('id', 'btitle',"is_delete")
      # 引數格式:?id=1&is_delete=True
    

除此外還可以向ORM的__方法一樣指定引數,但是要實現這個功能需要自定義FilterSet

有更加複雜定義方法 (FilterSet), 詳情見: 官方文件

異常處理

我們可以通過配置,指定某些函式處理程式中的異常資訊
步驟:

  1. 自定義一個異常處理函式:

    # myapp.my_exception.py
    
    
    from rest_framework.views import exception_handler
    from rest_framework.response import Response
    from django.db import DatabaseError
    
    def custom_exception_handler(exc, context):
    
        #1 呼叫系統方法,處理了APIException的異常,或者其子類異常
        response = exception_handler(exc, context)
    
        #2 判斷response是否有值
        if response is not None:
            response.data['status_code'] = response.status_code
        else:
            if isinstance(exc, DatabaseError):
                response = Response("資料庫大出血")
            else:
                response = Response("其他異常!")
    
        return response
    
  2. 將處理函式配置到REST_FRAMEWORK配置中

    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'myapp.my_exception.custom_exception_handler'
    }
    

生成API文件

該功能可以生成一個文件
操作流程:

  1. 安裝擴充套件
    $pip install coreapi
    
  2. 路由規則,在跟路由下:
    from rest_framework.documentation import include_docs_urls
    
    urlpatterns = [
    # ...
    path('^docs/', include_docs_urls(title='我的API文件'))    # title是標題,
    ]
    
  3. 新增備註資訊
    配置檢視集的__doc__和序列化器的help_textlabel
    
    class CourseViewSet(viewsets.ModelViewSet):
        """
        retrieve:
            返回指定course資訊
        list:
            返回course列表
        update:
            更新course資訊
        destroy:
            刪除course記錄
        create:
            建立course記錄
        partial_update:
            更新部分欄位
        """
    #在view中的資源類下,說明註釋資訊
    
    class Course(models.Model):
        name = models.CharField(max_length=64,verbose_name='課程名稱',help_text='課程名稱')
        ...
    #在model或者serializer中新增help_text欄位。它會顯示在api文件的欄位描述資訊中
    
    
  4. 開啟http://127.0.0.1:8008/docs/即可檢視文件

相關文章