前言
由於規畫上是做前後端分離的開發,django上採RESTful API,對於django本身著墨較少。以RESTful API為主,django本身只會用上admin、model。算是完全放棄templates了
不過django REST framework(後稱DRF)有些開發上仍會使用到部份django原生的功能
其實官方文件寫得相當清楚了!
這裡整理自己開發上遇到的一些坑及要注意的事項和一些使用筆記
可以使用左方的錨點快速定位
django
models.py
在model設定時間欄位,datetime.now不必加刮號。不然會變成model編譯時的時間
1 | from django.db import models |
若忘記給default,執行migrate
時也會跳出提示問你要直接加還是回來程式改
settings.py
有自定義的user model,讓django以自定義的user model為主
建立好自己要的model樣式後,在settings.py裡全局指定到該model
1 | AUTH_USER_MODEL = 'users.UserProfile' |
在settings.py
引入路徑,引用時就不必多打根目錄名字
如果有自己建立一個apps讓放所有app的話,可以在settings裡面引入路徑,這樣在使用時就可以少打一些字
1 | import sys |
app
在app裡面,取得內建的user model
1 | from django.contrib.auth import get_user_model |
若有自定義user model,django會優先去查找settings.py
的AUTH_USER_MODEL
,找不到再取得原生的
django REST framework
建議先照著DRF官方的Tutorial做一次,熟悉一下他的基本操作
會一路從最基本的功能帶到最終的功能:使用viewSet + 內建路由自動產生api路徑,只需使用少少的程式碼,就可以開發出一套RESTful API
而本身又可以自動產生說明文件,不必耗費心力維護文件
最好先熟悉django的CBV後在學習會比較容易上手
genericViewSet vs modelViewSet
modelViewSet預設就先做好所有crud,設好serializer和router後可直接使用
genericViewSet則沒有crud,要自行加入mixin才會有各crud功能
結論:crud全都要的話則使用modelViewSet;要自定義使用哪些crud的話,則使用genericViewSet
使用Router自動產生api路徑
django REST framework本身提供路由自動產生的功能
不必每新增一個功能就要自己寫路徑
在api的APP裡面新增一個urls.py
需配合viewset使用
1 | from django.urls import include,path |
在到django的url.py
新增
1 | path('api/',include('yourAPI.urls')), |
Nested Router
預設DRF無此功能,官方推薦使用drf-nested-routers套件
套件官方文件寫得很清楚了!只是一開始沒理解花了不少時間測試QQ
假設以發票中獎期別分別再對應底下所擁有的發票號碼,而後方再輸入primary key時可以得到該發票的明細
可以產生/invoicesLottery/{sn}/myInvoicesList/{pk}/
這種Router
當網址輸入
/invoicesLottery/
→得到所有期別的中獎號碼/invoicesLottery/{sn}
→得到指定期別的中獎號碼明細(頭獎、二獎、三獎號碼…)/invoicesLottery/{sn}/myInvoicesList/
→得到自己當期已儲存的發票清單/invoicesLottery/{sn}/myInvoicesList/{id}/
→得到該發票的明細資料(比如pk、建立時間、消費記錄等)
而對應層級中若各別需指定權限,依各自指定的ViewSet而定
比如在取得第一層的/invoicesLottery/
時是不限定權限,而在取得第二層的/myInvoicesList/
時需驗證權限
那在invoicesLotteryViewSet
中就不必寫permission_classes = (IsAuthenticated,)
;在第二層的myInvoicesListViewSet
寫上permission_classes = (IsAuthenticated,)
作法如下
url.py
1 | from rest_framework_nested import routers |
views.py
第一層invoicesLotteryViewSet
的寫法可以跟一般ViewSet一樣,不必特意改
而放在第二層的invoicesViewSet
,則需要修改get_queryset
,補上傳入的參數
1 | class invoicesLotteryViewSet(ModelViewSet): |
關於serializer 基礎
修改DRF 頁面底下的顯示名稱
在資料欄上加入label="文字"
,可以修改drf的頁面底下的顯示名稱
等同於django model的verbose
serializer 的datetime可以自定義時間格式
1 | # 若不指定,預設為2018-07-02T21:42:53 |
serializers.SerializerMethodField
可以自定義檢查條件
function名稱固定為get_欄位名稱
1 | abc = serializers.SerializerMethodField() |
搜尋及過濾 filter, search
需要配合django
原生的filter
功能
在viewset中配置
1 | from django_filters.restframework import DjangoFilterBackend # 要記得import成.restframework的 |
分頁 Pagination
1 | from rest_framework.pagination import PageNumberPagination |
接著再要使用此calss做分頁條件的viewset中
新增如下
1 | pagination_class = productsPagination |
Pagination 增加總頁數(或自定義回傳的資料)
1 | from rest_framework.pagination import PageNumberPagination |
自動產生說明文件
在urls.py
裡
1 | from rest_framework.documentation import include_docs_urls |
在model或serializer上,寫help_text
,文檔裡會自動新增至description裡
※文件的資訊是從serializer來,若跳過此步自定義api的話,無法自動產生
對於需要權限的api,左下方的Authentication可以測試登入
限制API訪問速率(避免爬蟲過量爬取)
在settings.py
裡新增
1 | REST_FRAMEWORK = { |
AnonRateThrottle:暱名用戶,用ip來判
UserRateThrottle:登入用戶,用session來判斷
超過限制後,會回傳http 429錯誤
修改API傳入參數
預設DRF是以Primary Key為主,若想要以其他欄位當參數的話
在viewset裡輸入
1 | lookup_field='mobile' |
傳入參數就會由/api/users/{id}
變成/api/users/{mobile}
嘍!
POST資料,獲得當下USER
寫在serializer裡
1 | #user即model的user變數 |
serializer呼叫serializer時,將圖片網址補上域名
serializer預設是不會自動加上域名
平常使用時,都會自動傳入。所以不必另外寫
要在補上參數context={'request': self.context['request']}
原理:原始碼在context有看到request時,就會自動將域名補上
cache
使用套件drf-extensions
,有強化許多功能!具體細節參考官方文件
from rest_framework_extensions.cache.mixins import CacheResponseMixin
在viewset繼承CacheResponseMixin
繼承cache,盡量放在第一個
signals
apps.py
1 | from django.apps import AppConfig |
新建signals.py,裡面寫所有signals
1 | from django.db.models.signals import post_save |
動態調整permission_classes、serializer_class
假設有一個使用情況:
UserViewSet在get與post時有權限的分別
create(post)建立帳號時權限是AllowAny
retrieve(get)取得個人資料時是IsAuthenticated
1 | class UserViewset(CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): |