Django REST framework
一個強大靈活的Django
工具包,提供了便捷的REST API
開發框架
我們用傳統的django
也可以實現REST風格的api
,但是頂不住Django REST framework
實現起來太簡單了
什麼REST API風格
主要需要注意這幾個:
- api部署的域名, 主域名或者專有域名
- 版本, 通過url地址或者請求頭accept
- 路徑, 只能有名詞, 不能有動詞
- http請求動詞, get, post, update, delete
- 狀態, 200, 201, 204, 400, 401, 403, 404
- 返回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
中的view
和form
在進行了通用性設計。由於使用REST API,我們可以統一使用一樣的請求方式,請求引數和請求體,而響應體和狀態碼也是確定。所以Django RST framework
的複用性極高,而且Django RST framework
提供了從簡單到複雜的封裝類,我們只需要直接使用這些類或繼承這些類重寫部分方法,就可以享受到便利性和可擴充套件性。
為了顯示Django RST framework
簡單易用,用展示封裝程度比較高的類實現一個五個基本功能(get一個、get全部、post建立、put修改單個、delete刪除單個)
-
安裝
Django RST framework
$pip install djangorestframework
-
修改setting檔案
在settings.INSTALLED_APPS
中新增rest_framework
注:新增這個app可以為我們提供測試頁面, 假如是使用postman等工具進行測試的話可以不用新增
-
建立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
-
執行遷移命令
$python manage.py makemigrations $python manage.py migrate
-
定義序列化器
在app目錄下建立一個serializers.py
,在裡面定義序列化器:from rest_framework.serializers import ModelSerializer from rest_test.models import Author class AuthorSerialize(ModelSerializer): class Meta: model = Author fields = "__all__"
-
使用檢視集
在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
-
定義路由規則
在根路由中定義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
-
啟動django,並訪問
/authors
我們就可以看到這樣的頁面,可以對資料進行增刪改查了:
序列化器
序列化器的主要作用就是進行資料轉換,即序列化(python的資料型別轉換為json等資料型別)和反序列化(json等資料型別轉換為python的資料型別)
在反序列化的過程中我們還可以手動驗證資料是否合法, 可以與model相結合,完成對資料庫的操作
所以說,序列化器和django
的Form
非常像,所以假如會django
的Form
的話,應該很好理解
普通的序列化器
在定程度上可以類比
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等資料。
序列化一個
步驟:
- 匯入
AuthorSerialize
和Author
模型 - 使用
get
獲取Author
的資料 - 將資料傳給
AuthorSerialize
的instance引數
- 使用
.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
處理資料一樣。
一般操作
此部分還不涉及將資料儲存到資料庫
流程:
- 獲得資料
- 將資料傳入到序列化器的
data
引數中 - 呼叫返回物件的
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_valid
和django form
的is_valid
一樣,可以檢驗資料是否符合我們在序列化器中定義的一樣,檢驗成功是返回True
,否則返回False
。- 假如指定
raise_exception
為True
,那麼當資料不合法時,丟擲異常,我們可以通過error_messages
引數提示資訊(見: 自定義錯誤資訊)。 - 假如不想要丟擲異常,我們也可以通過
.errors
獲取錯誤資訊(這是一個OrderedDict
的子類物件)如這裡的result.errors
。
利用序列化器進行建立和更新資料
終於講到如何將資料儲存到資料庫了。
用過django ModelForm
的都知道,只要將資料傳入到form
中,假如資料通過,就可以通過.save()
方法將資料儲存到資料庫,指定instance
引數可一個更新資料。而序列化器的使用方式樣是這樣,不過我們現在用的是叫較為低階的序列化器,需要我們自定義建立資料和更新資料的邏輯。
建立資料
使用步驟:
- 獲得資料
- 將資料傳入到序列化器的
data
引數中 - 呼叫返回物件的
is_valid()
方法 - 為序列化器定義
create
方法 - 呼叫
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請求方式是更新全部資料
使用步驟:
- 獲得資料
- 將資料傳入到序列化器的
data
引數中, 要更新的資料傳入instance
引數 - 呼叫返回物件的
is_valid()
方法 - 為序列化器定義
update
方法 - 呼叫
save
方法,執行建立資料的函式(會呼叫update
函式)
只要序列化器中同時傳入data
和instance
引數,它就會呼叫更新的方法(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
十分相似
在上面使用的例子就用到了該序列化器
說到底,它就是將幹了這幾件事:
- 我們的model欄位根據對應關係轉化為serializers中的欄位
- 將model欄位的約束條件變為serializers欄位的約束條件
- 內建了
create
方法和update
方法
簡單使用
簡直是簡簡單單:
class AuthorModelSerialize(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__"
Meta類
從例子中看出,Meta
類是關鍵,它決定著 要序列化的欄位、用哪個模型、錯誤資訊、約束條件等重要的引數。
下面我們看看Meta類可以指定什麼內容:
還是那句話,真的和
ModelForm
十分相似
model
指定用哪個Modelfields
使用模型中的哪些欄位,推薦利用元組顯式指定,但可以將值指定為__all__
(表示全部欄位)
不能和exclude
一起使用,否則報錯:Cannot set both 'fields' and 'exclude' options on serializer xxx.exclude
不使用模型中的哪些欄位
不能和fields
一起使用,否則報錯:Cannot set both 'fields' and 'exclude' options on serializer xxx.depth
指定關聯深度,是一個數字
注意,關聯欄位預設返回的是主鍵,即rest_framework.serializers.PrimaryKeyRelatedField
read_only_fields
只讀欄位,主鍵預設只讀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
是一個異常,有兩個引數detail
和code
,前者接收異常的描述資訊,後者接收異常名(預設為"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開頭且地址是廣東省的才能通過驗證")
自定義驗證器
分兩步:
- 定義
使用驗證器時,傳入的資料已經轉換為python的資料型別了def check_bpub_date(date): if date.year < 2015: raise serializers.ValidationError("日期不能小於2015年") return date
- 使用
通過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
屬性指定
自定義錯誤資訊
- 內建錯誤通過
error_messages
引數指定 - 手動丟擲的異常,通過異常資訊指定
錯誤資訊是可以用一個函式專門處理的,詳情請看: 異常處理
如:
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
的檢視種類比序列化器還多,從一級檢視到檢視集,每一個都是在之前的檢視基礎上進行了封裝,至於用哪個,全憑自己的喜好了。
詳情見下圖:
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
,返回一個response
,Django REST framework
也不例外。
request
DRF
的Request
類擴充套件了django
預設的HttpReques
t,它主要有兩個屬性:
Request.data
得到請求體的資料(表單資料和JSON資料都行)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 | 該請求已成功,並因此建立了一個新的資源, 一般為POST 或PUT 成功後返回 |
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.APIView
是django.views.generic.base.View
的子類,用起來和普通的傳統的繼承View的寫法差不多
列表檢視
列表檢視的寫法:
- 定義一個類,繼承
APIView
- 定義方法(
get
,post
) - 從
request
中獲取資料 - 利用 序列化器 序列化資料或檢驗資料入庫
- 返回(除非丟擲異常,否則返回的都是
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())
]
詳情檢視
在原來的基礎上多了主鍵,用來操作單個資料:
- 同樣繼承
APIView
- 定義
get
put
delete
四個方法 - 使用
get_object_or_404
取得要操作的物件(不用我們處理異常) - 返回資料
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
有更好地方法定義。
介紹
GenericAPIView
是APIView
的子類,它將我們用到的序列化器和模型已經獲取model物件的方法進行了整合,即新增了常用的行為和屬性。雖然我們也可以做到這樣,但是它已經做好了,那麼我們只需要瞭解規則就可以直接使用了。
GenericAPIView
有4個重要屬性和3個重要方法:
- 屬性
1.queryset
: 通用的資料集,通常是XXX.objects.all()
2.serializer_class
: 通用的序列化器
3.lookup_field
: 主鍵的名稱,預設是pk
,主鍵,請注意與lookup_url_kwarg
區分
4.lookup_url_kwarg
:url
引數中的標識,如:/books/1
若:lookup_url_kwarg
為books_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
除此之外,你還可以根據需求任意組合
使用起來也特別簡單:
- 繼承
GenericAPIView
和對應的Mixin
- 定義方法(
get
,post
,put
,delete
) - 返回
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
即可。
特點:
- 可以將一組相關的操作,放在一個類中進行完成
- 處理方法變化了:
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
中
使用檢視集
下面介紹四中檢視集類的使用方式
-
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
函式那裡處理 -
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
的程式碼 -
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"})) ]
-
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的作者:
- 定義檢視集
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)
- 寫路由對映關係
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)
至於路由如何寫,見下文的路由部分
總結一下
捋一下思路
- 一級檢視只是繼承了
View
,用法和原生Django
十分相似,幾乎所有方法都要我們寫。 - 二級檢視將序列化器和模型資料定義在了類屬性中,並且提供了獲取單個模型的方法(
get_object
),簡化了部分操作。最重要的是它可以與Mixin
類結合,能夠減少的部分工作,但是仍然需要自定義方法,然後再方法中呼叫Mixin
類的方法。 - 三級檢視則是父類直接提供方法,我們繼承該類就可以使用了。
- 這三種檢視定義方法,有一個明顯的缺點 (可能吧) : 我們必須要定義多個檢視類(列表檢視和詳細檢視,要新增額外方法的話,可能還要增加檢視數量)
- 檢視集可以在路由中進行路由對映,因此可以把所有處理方法都定義在同一個類裡面,而且它也提供了從簡單到複雜的檢視集類,方便我們的使用
- 至於在實際開發過程中,要用哪個類,全憑自己選擇吧,最多多寫幾段程式碼而已
路由
SimpleRouter
一般使用:
router = SimpleRouter()
- 呼叫
register
方法 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
此部分講的是認證內容,即如何確定你是你??
有兩種配置方式:
- 在
settings
中配置全域性的
如:REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ) }
- 在檢視中配置本檢視的
from rest_framework.authentication import SessionAuthentication, BasicAuthentication class BookInfoModelViewSet(ModelViewSet): authentication_classes = (SessionAuthentication, BasicAuthentication) pass
一般來說我們常用有這幾種驗證方式:
BasicAuthentication
基於HTTP的認證方式,要求驗證的時候會彈出一個框來,讓你輸入賬號和密碼,一般只在開發測試時使用SessionAuthentication
基於Session的認證,這裡應該用的時django自帶的AUTH元件TokenAuthentication
基於Token的HTTP認證方案,即生成一個令牌,作為標識,請求頭帶上Authorization: Token token的值
即可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
- 配置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', # 管理員使用者 ) }
- 執行遷移命令,這時會生成一個
authtoken_token
表$python manage.py migrate
- 編寫登入檢視
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)
- 配置路由
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()) ]
- 將使用者名稱和密碼利用
POST
方式提交給token
後就會返回token
獲得token
後,將token
放在請求頭即可。
如:獲取token
為:323ce6f331c7aa65836ad48169037f001ce7e18f
那麼請求頭就為:Authorization: Token 323ce6f331c7aa65836ad48169037f001ce7e18f
JSON Web Token Authentication
-
安裝
djangorestframework-simplejwt
,這是目前官網推薦的python jwt庫$pip install djangorestframework-simplejwt
-
配置驗證器為:
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', # 管理員使用者 ) }
-
配置jwt驗證器
# settings.py import datetime SIMPLE_JWT = { # token有效時長 'ACCESS_TOKEN_LIFETIME': datetime.timedelta(minutes=30), # token重新整理後的有效時間 'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1) }
-
定義路由規則
由於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'), ]
-
將使用者名稱和密碼提交到
POST api/token/
返回的資訊包括refresh
和access
兩個欄位。
其中refresh
是用於重新整理token
的(每個token
都是有時間限制的,過了時間就失效了)
access
是用於後續的請求時驗證身份的。 -
將
access
的值放入請求頭中:
如:獲取的access
為eyJ0eiJIUiJ9.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_staff
為True
的使用者執行任何請求(說明這是一個管理員賬號) -
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
- 安裝
django-filter
$pip install django-filter
- 將
django-filter
配置到INSTALLED_APPS
INSTALLED_APPS = [ # ... 'django_filters', ]
- 配置
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
), 詳情見: 官方文件
異常處理
我們可以通過配置,指定某些函式處理程式中的異常資訊
步驟:
-
自定義一個異常處理函式:
# 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
-
將處理函式配置到
REST_FRAMEWORK
配置中REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'myapp.my_exception.custom_exception_handler' }
生成API文件
該功能可以生成一個文件
操作流程:
- 安裝擴充套件
$pip install coreapi
- 路由規則,在跟路由下:
from rest_framework.documentation import include_docs_urls urlpatterns = [ # ... path('^docs/', include_docs_urls(title='我的API文件')) # title是標題, ]
- 新增備註資訊
配置檢視集的__doc__
和序列化器的help_text
或label
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文件的欄位描述資訊中
- 開啟
http://127.0.0.1:8008/docs/
即可檢視文件