在開發Web應用中,有兩種應用模式:css
爲了在團隊內部造成共識、防止我的習慣差別引發的混亂,咱們須要找到一種你們都以爲很好的接口實現規範,並且這種規範可以讓後端寫的接口,用途一目瞭然,減小雙方之間的合做成本。html
目前市面上大部分公司開發人員使用的接口服務架構主要有:restful、rpc。前端
rpc: 翻譯成中文:遠程過程調用[遠程服務調用].python
http://www.lufei.com/apimysql
post請求linux
action=get_all_student¶ms=301&sex=1git
接口多了,對應函數名和參數就多了,前端在請求api接口時,就會比較難找.容易出現重複的接口github
restful: 翻譯成中文: 資源狀態轉換.web
把後端全部的數據/文件都當作資源.sql
那麼接口請求數據,本質上來講就是對資源的操做了.
web項目中操做資源,無非就是增刪查改.因此要求在地址欄中聲明要操做的資源是什麼,而後經過http請求動詞來講明對資源進行哪種操做.
POST http://www.lufei.com/api/students/ 添加學生數據
GET http://www.lufei.com/api/students/ 獲取全部學生
DELETE http://www.lufei.com/api/students/
REST全稱是Representational State Transfer,中文意思是表述(編者注:一般譯爲表徵)性狀態轉移。 它首次出如今2000年Roy Fielding的博士論文中。
RESTful是一種定義Web API接口的設計風格,尤爲適用於先後端分離的應用模式中。
這種風格的理念認爲後端開發任務就是提供數據的,對外提供的是數據資源的訪問接口,因此在定義接口時,客戶端訪問的URL路徑就表示這種要操做的數據資源。
而對於數據資源分別使用POST、DELETE、GET、UPDATE等請求動做來表達對數據的增刪查改。
請求方法 | 請求地址 | 後端操做 |
---|---|---|
GET | /students | 獲取全部學生 |
POST | /students | 增長學生 |
GET | /students/
|
獲取編號爲pk的學生 |
PUT | /students/
|
修改編號爲pk的學生 |
DELETE | /students/
|
刪除編號爲pk的學生 |
事實上,咱們能夠使用任何一個框架均可以實現符合restful規範的API接口。
參考文檔:http://www.runoob.com/w3cnote/restful-architecture.html
api接口開發,最核心最多見的一個過程就是序列化,所謂序列化就是把數據轉換格式,序列化能夠分兩個階段:
序列化: 把咱們識別的數據轉換成指定的格式提供給別人。
例如:咱們在django中獲取到的數據默認是模型對象,可是模型對象數據沒法直接提供給前端或別的平臺使用,因此咱們須要把數據進行序列化,變成字符串或者json數據,提供給別人。
反序列化:把別人提供的數據轉換/還原成咱們須要的格式。
例如:前端js提供過來的json數據,對於python而言就是字符串,咱們須要進行反序列化換成模型類對象,這樣咱們才能把數據保存到數據庫中。
核心思想: 縮減編寫api接口的代碼
Django REST framework是一個創建在Django基礎之上的Web 應用開發框架,能夠快速的開發REST API接口應用。在REST framework中,提供了序列化器Serialzier的定義,能夠幫助咱們簡化序列化與反序列化的過程,不只如此,還提供豐富的類視圖、擴展類、視圖集來簡化視圖的編寫工做。REST framework還提供了認證、權限、限流、過濾、分頁、接口文檔等功能支持。REST framework提供了一個API 的Web可視化界面來方便查看測試接口。
中文文檔:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework
github: https://github.com/encode/django-rest-framework/tree/master
DRF須要如下依賴:
DRF是以Django擴展應用的方式提供的,因此咱們能夠直接利用已有的Django環境而無需重新建立。(若沒有Django環境,須要先建立環境安裝Django)
前提是已經安裝了django,建議安裝在虛擬環境
# mkvirtualenv drfdemo -p python3 # pip install django pip install djangorestframework pip install pymysql
linux 複製 shift+insert
cd ~/Desktop django-admin startproject drfdemo
使用pycharm打開項目,設置虛擬環境的解析器,並修改manage.py中的後綴參數。
在settings.py的INSTALLED_APPS中添加'rest_framework'。
INSTALLED_APPS = [ ... 'rest_framework', ]
接下來就可使用DRF提供的功能進行api接口開發了。在項目中若是使用rest_framework框架實現API接口,主要有如下三個步驟:
接下來,咱們快速體驗下四天後咱們學習完成drf之後的開發代碼。接下來代碼不須要理解,看步驟。
class Student(models.Model): # 模型字段 name = models.CharField(max_length=100,verbose_name="姓名") sex = models.BooleanField(default=1,verbose_name="性別") age = models.IntegerField(verbose_name="年齡") class_null = models.CharField(max_length=5,verbose_name="班級編號") description = models.TextField(max_length=1000,verbose_name="個性簽名") class Meta: db_table="tb_student" verbose_name = "學生" verbose_name_plural = verbose_name
爲了方便測試,因此咱們能夠先建立一個數據庫。
mysql -uroot -p; create database students charset=utf8;
把students子應用添加到INSTALL_APPS中
初始化數據庫鏈接
安裝pymysql pip install pymysql
主引用中__init__.py
設置使用pymysql做爲數據庫驅動
import pymysql pymysql.install_as_MySQLdb()
settings.py配置文件中設置mysql的帳號密碼
DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # }, 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': "students", "HOST": "127.0.0.1", "PORT": 3306, "USER": "root", "PASSWORD":"123", } }
終端下,執行數據遷移。
python manage.py makemigrations python manage.py migrate
錯誤列表
# 執行數據遷移 python manage.py makemigrations 報錯以下:
解決方案:
註釋掉 backends/mysql/base.py中的35和36行代碼。
# 執行數據遷移發生如下錯誤:
解決方法:
backends/mysql/operations.py146行裏面新增一個行代碼:
例如,在django項目中建立學生子應用。
python manage.py startapp students
在syudents應用目錄中新建serializers.py用於保存該應用的序列化器。
建立一個StudentModelSerializer用於序列化與反序列化。
# 建立序列化器類,回頭會在試圖中被調用 class StudentModelSerializer(serializers.ModelSerializer): class Meta: model = Student fields = "__all__"
在students應用的views.py中建立視圖StudentViewSet,這是一個視圖集合。
from rest_framework.viewsets import ModelViewSet from .models import Student from .serializers import StudentModelSerializer # Create your views here. class StudentViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer
在students應用的urls.py中定義路由信息。
from . import views from rest_framework.routers import DefaultRouter # 路由列表 urlpatterns = [] router = DefaultRouter() # 能夠處理視圖的路由器 router.register('students', views.StudentViewSet) # 向路由器中註冊視圖集 urlpatterns += router.urls # 將路由器中的因此路由信息追到到django的路由列表中
最後把students子應用中的路由文件加載到總路由文件中.
from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path("stu/",include("students.urls")), ]
運行當前程序(與運行Django同樣)
python manage.py runserver
在瀏覽器中輸入網址127.0.0.1:8000,能夠看到DRF提供的API Web瀏覽頁面:
1)點擊連接127.0.0.1:8000/stu/students 能夠訪問獲取全部數據的接口,呈現以下頁面:
2)在頁面底下表單部分填寫學生信息,能夠訪問添加新學生的接口,保存學生信息:
點擊POST後,返回以下頁面信息:
3)在瀏覽器中輸入網址127.0.0.1:8000/stu/students/5/,能夠訪問獲取單一學生信息的接口(id爲5的學生),呈現以下頁面:
4)在頁面底部表單中填寫學生信息,能夠訪問修改學生的接口:
點擊PUT,返回以下頁面信息:
5)點擊DELETE按鈕,能夠訪問刪除學生的接口:
返回,以下頁面:
做用:
Django REST framework中的Serializer使用類來定義,須繼承自rest_framework.serializers.Serializer。
接下來,爲了方便演示序列化器的使用,咱們先建立一個新的子應用sers
python manage.py startapp sers
咱們已有了一個數據庫模型類students/models.py
models.py
from django.db import models # Create your models here. class Student(models.Model): # 模型字段 name = models.CharField(max_length=100,verbose_name="姓名",help_text="提示文本:帳號不能爲空!") sex = models.BooleanField(default=True,verbose_name="性別") age = models.IntegerField(verbose_name="年齡") class_null = models.CharField(max_length=5,verbose_name="班級編號") description = models.TextField(verbose_name="個性簽名") class Meta: db_table="tb_student" verbose_name = "學生" verbose_name_plural = verbose_name
咱們想爲這個模型類提供一個序列化器,能夠定義以下:
from rest_framework import serializers # 聲明序列化器,全部的序列化器都要直接或者間接繼承於 Serializer # 其中,ModelSerializer是Serializer的子類,ModelSerializer在Serializer的基礎上進行了代碼簡化 class StudentSerializer(serializers.Serializer): """學生信息序列化器""" # 1. 須要進行數據轉換的字段 id = serializers.IntegerField() name = serializers.CharField() age = serializers.IntegerField() sex = serializers.BooleanField() description = serializers.CharField() # 2. 若是序列化器集成的是ModelSerializer,則須要聲明調用的模型信息 # 3. 驗證代碼 # 4. 編寫添加和更新模型的代碼
注意:serializer不是隻能爲數據庫模型類定義,也能夠爲非數據庫模型類的數據定義。serializer是獨立於數據庫以外的存在。
經常使用字段類型:
字段 | 字段構造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(max*length=50, min_length=None, allow_blank=False) 正則字段,驗證正則模式 [a-zA-Z0-9*-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format='hex_verbose') format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
IPAddressField | IPAddressField(protocol='both', unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位數 decimal_palces: 小數點位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices與Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
選項參數:
參數名稱 | 做用 |
---|---|
max_length | 最大長度 |
min_lenght | 最小長度 |
allow_blank | 是否容許爲空 |
trim_whitespace | 是否截斷空白字符 |
max_value | 最小值 |
min_value | 最大值 |
通用參數:
參數名稱 | 說明 |
---|---|
read_only | 代表該字段僅用於序列化輸出,默認False |
write_only | 代表該字段僅用於反序列化輸入,默認False |
required | 代表該字段在反序列化時必須輸入,默認True |
default | 反序列化時使用的默認值 |
allow_null | 代表該字段是否容許傳入None,默認False |
validators | 該字段使用的驗證器 |
error_messages | 包含錯誤編號與錯誤信息的字典 |
label | 用於HTML展現API頁面時,顯示的字段名稱 |
help_text | 用於HTML展現API頁面時,顯示的字段幫助提示信息 |
定義好Serializer類後,就能夠建立Serializer對象了。
Serializer的構造方法爲:
Serializer(instance=None, data=empty, **kwarg)
說明:
1)用於序列化時,將模型類對象傳入instance參數
2)用於反序列化時,將要被反序列化的數據傳入data參數
3)除了instance和data參數外,在構造Serializer對象時,還可經過context參數額外添加數據,如
serializer = AccountSerializer(account, context={'request': request})
經過context參數附加的數據,能夠經過Serializer對象的context屬性獲取。
序列化器的使用分兩個階段:
1) 先查詢出一個學生對象
from students.models import Student student = Student.objects.get(id=3)
2) 構造序列化器對象
from .serializers import StudentSerializer serializer = StudentSerializer(instance=student)
3)獲取序列化數據
經過data屬性能夠獲取序列化後的數據
serializer.data # {'id': 4, 'name': '小張', 'age': 18, 'sex': True, 'description': '猴賽雷'}
完整視圖代碼:
views.py
from django.views import View from students.models import Student from .serializers import StudentSerializer from django.http.response import JsonResponse class StudentView(View): """使用序列化器序列化轉換單個模型數據""" def get(self,request,pk): # 獲取數據 student = Student.objects.get(pk=pk) # 數據轉換[序列化過程] serializer = StudentSerializer(instance=student) print(serializer.data) # 響應數據 return JsonResponse(serializer.data)
4)若是要被序列化的是包含多條數據的查詢集QuerySet,能夠經過添加many=True參數補充說明
"""使用序列化器序列化轉換多個模型數據""" def get(self,request): # 獲取數據 student_list = Student.objects.all() # 轉換數據[序列化過程] # 若是轉換多個模型對象數據,則須要加上many=True serializer = StudentSerializer(instance=student_list,many=True) print( serializer.data ) # 序列化器轉換後的數據 # 響應數據給客戶端 # 返回的json數據,若是是列表,則須要聲明safe=False return JsonResponse(serializer.data,safe=False) # 訪問結果: # [OrderedDict([('id', 1), ('name', 'xiaoming'), ('age', 20), ('sex', True), ('description', '測試')]), OrderedDict([('id', 2), ('name', 'xiaohui'), ('age', 22), ('sex', True), ('description', '後面來的測試')]), OrderedDict([('id', 4), ('name', '小張'), ('age', 18), ('sex', True), ('description', '猴賽雷')])]
使用序列化器進行反序列化時,須要對數據進行驗證後,才能獲取驗證成功的數據或保存成模型類對象。
在獲取反序列化的數據前,必須調用is_valid()方法進行驗證,驗證成功返回True,不然返回False。
驗證失敗,能夠經過序列化器對象的errors屬性獲取錯誤信息,返回字典,包含了字段和字段的錯誤。若是是非字段錯誤,能夠經過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯誤字典中的鍵名。
驗證成功,能夠經過序列化器對象的validated_data屬性獲取數據。
在定義序列化器時,指明每一個字段的序列化類型和選項參數,自己就是一種驗證行爲。
如咱們前面定義過的BookInfoSerializer
class BookInfoSerializer(serializers.Serializer): """圖書數據序列化器""" id = serializers.IntegerField(label='ID', read_only=True) btitle = serializers.CharField(label='名稱', max_length=20) bpub_date = serializers.DateField(label='發佈日期', required=False) bread = serializers.IntegerField(label='閱讀量', required=False) bcomment = serializers.IntegerField(label='評論量', required=False) image = serializers.ImageField(label='圖片', required=False)
經過構造序列化器對象,並將要反序列化的數據傳遞給data構造參數,進而進行驗證
測試:
from booktest.serializers import BookInfoSerializer data = {'bpub_date': 123} serializer = BookInfoSerializer(data=data) serializer.is_valid() # 返回False serializer.errors # {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]} serializer.validated_data # {} data = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # True serializer.errors # {} serializer.validated_data # OrderedDict([('btitle', 'python')])
is_valid()方法還能夠在驗證失敗時拋出異常serializers.ValidationError,能夠經過傳遞raise_exception=True參數開啓,REST framework接收到此異常,會向前端返回HTTP 400 Bad Request響應。
# Return a 400 response if the data was invalid. serializer.is_valid(raise_exception=True)
若是以爲這些還不夠,須要再補充定義驗證行爲,可使用如下三種方法:
對<field_name>
字段進行驗證,如
class BookInfoSerializer(serializers.Serializer): """圖書數據序列化器""" ... def validate_btitle(self, value): if 'django' not in value.lower(): raise serializers.ValidationError("圖書不是關於Django的") return value
測試
from booktest.serializers import BookInfoSerializer data = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # False serializer.errors # {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}
在序列化器中須要同時對多個字段進行比較驗證時,能夠定義validate方法來驗證,如
class BookInfoSerializer(serializers.Serializer): """圖書數據序列化器""" ... def validate(self, attrs): bread = attrs['bread'] bcomment = attrs['bcomment'] if bread < bcomment: raise serializers.ValidationError('閱讀量小於評論量') return attrs
測試
from booktest.serializers import BookInfoSerializer data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20} s = BookInfoSerializer(data=data) s.is_valid() # False s.errors # {'non_field_errors': [ErrorDetail(string='閱讀量小於評論量', code='invalid')]}
在字段中添加validators選項參數,也能夠補充驗證行爲,如
def about_django(value): if 'django' not in value.lower(): raise serializers.ValidationError("圖書不是關於Django的") class BookInfoSerializer(serializers.Serializer): """圖書數據序列化器""" id = serializers.IntegerField(label='ID', read_only=True) btitle = serializers.CharField(label='名稱', max_length=20, validators=[about_django]) bpub_date = serializers.DateField(label='發佈日期', required=False) bread = serializers.IntegerField(label='閱讀量', required=False) bcomment = serializers.IntegerField(label='評論量', required=False) image = serializers.ImageField(label='圖片', required=False)
測試:
from booktest.serializers import BookInfoSerializer data = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # False serializer.errors # {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}
前面的驗證數據成功後,咱們可使用序列化器來完成數據反序列化的過程.這個過程能夠把數據轉成模型類對象.
能夠經過實現create()和update()兩個方法來實現。
class BookInfoSerializer(serializers.Serializer): """圖書數據序列化器""" ... def create(self, validated_data): """新建""" return BookInfo(**validated_data) def update(self, instance, validated_data): """更新,instance爲要更新的對象實例""" instance.btitle = validated_data.get('btitle', instance.btitle) instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date) instance.bread = validated_data.get('bread', instance.bread) instance.bcomment = validated_data.get('bcomment', instance.bcomment) return instance
若是須要在返回數據對象的時候,也將數據保存到數據庫中,則能夠進行以下修改
class BookInfoSerializer(serializers.Serializer): """圖書數據序列化器""" ... def create(self, validated_data): """新建""" return BookInfo.objects.create(**validated_data) def update(self, instance, validated_data): """更新,instance爲要更新的對象實例""" instance.btitle = validated_data.get('btitle', instance.btitle) instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date) instance.bread = validated_data.get('bread', instance.bread) instance.bcomment = validated_data.get('bcomment', instance.bcomment) instance.save() return instance
實現了上述兩個方法後,在反序列化數據的時候,就能夠經過save()方法返回一個數據對象實例了
book = serializer.save()
若是建立序列化器對象的時候,沒有傳遞instance實例,則調用save()方法的時候,create()被調用,相反,若是傳遞了instance實例,則調用save()方法的時候,update()被調用。
from db.serializers import BookInfoSerializer data = {'btitle': '封神演義'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # True serializer.save() # <BookInfo: 封神演義> from db.models import BookInfo book = BookInfo.objects.get(id=2) data = {'btitle': '倚天劍'} serializer = BookInfoSerializer(book, data=data) serializer.is_valid() # True serializer.save() # <BookInfo: 倚天劍> book.btitle # '倚天劍'
1) 在對序列化器進行save()保存時,能夠額外傳遞數據,這些數據能夠在create()和update()中的validated_data參數獲取到
# request.user 是django中記錄當前登陸用戶的模型對象 serializer.save(owner=request.user)
2)默認序列化器必須傳遞全部required的字段,不然會拋出驗證異常。但是咱們可使用partial參數來容許部分字段更新
# Update `comment` with partial data serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
若是咱們想要使用序列化器對應的是Django的模型類,DRF爲咱們提供了ModelSerializer模型類序列化器來幫助咱們快速建立一個Serializer類。
ModelSerializer與常規的Serializer相同,但提供了:
好比咱們建立一個BookInfoSerializer
class BookInfoSerializer(serializers.ModelSerializer): """圖書數據序列化器""" class Meta: model = BookInfo fields = '__all__'
咱們能夠在python manage.py shell中查看自動生成的BookInfoSerializer的具體實現
>>> from booktest.serializers import BookInfoSerializer >>> serializer = BookInfoSerializer() >>> serializer BookInfoSerializer(): id = IntegerField(label='ID', read_only=True) btitle = CharField(label='名稱', max_length=20) bpub_date = DateField(allow_null=True, label='發佈日期', required=False) bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=-2147483648, required=False) bcomment = IntegerField(label='評論量', max_value=2147483647, min_value=-2147483648, required=False) image = ImageField(allow_null=True, label='圖片', max_length=100, required=False)
1) 使用fields來明確字段,__all__
表名包含全部字段,也能夠寫明具體哪些字段,如
class BookInfoSerializer(serializers.ModelSerializer): """圖書數據序列化器""" class Meta: model = BookInfo fields = ('id', 'btitle', 'bpub_date')
2) 使用exclude能夠明確排除掉哪些字段
class BookInfoSerializer(serializers.ModelSerializer): """圖書數據序列化器""" class Meta: model = BookInfo exclude = ('image',)
3) 顯示指明字段,如:
class HeroInfoSerializer(serializers.ModelSerializer): hbook = BookInfoSerializer() class Meta: model = HeroInfo fields = ('id', 'hname', 'hgender', 'hcomment', 'hbook')
4) 指明只讀字段
能夠經過read_only_fields指明只讀字段,即僅用於序列化輸出的字段
class BookInfoSerializer(serializers.ModelSerializer): """圖書數據序列化器""" class Meta: model = BookInfo fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment') read_only_fields = ('id', 'bread', 'bcomment')
咱們可使用extra_kwargs參數爲ModelSerializer添加或修改原有的選項參數
class BookInfoSerializer(serializers.ModelSerializer): """圖書數據序列化器""" class Meta: model = BookInfo fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment') extra_kwargs = { 'bread': {'min_value': 0, 'required': True}, 'bcomment': {'min_value': 0, 'required': True}, } # BookInfoSerializer(): # id = IntegerField(label='ID', read_only=True) # btitle = CharField(label='名稱', max_length=20) # bpub_date = DateField(allow_null=True, label='發佈日期', required=False) # bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=0, required=True) # bcomment = IntegerField(label='評論量', max_value=2147483647, min_value=0, required=True)
django中一共提供了兩種視圖:分別是函數視圖[function view]和類視圖[class view]。
# 函數視圖 def login(request): """登陸功能""" # 登陸功能代碼 return Response({「message」:"ok"})
接下來,方便學習,因此咱們建立一個新的子應用clsview
python manage.py startapp clsview
函數視圖,實現功能,須要手動判斷用戶的請求動做,並且當函數視圖數量多了,就不要概括整理。
"""函數視圖的登陸功能""" from django.http import HttpResponse def login_form(request): """顯示登陸頁面""" html = """ <form method="post" action="/clsview/login_data/"> 帳號: <input type="text" name="usernane"><br><br> 密碼: <input type="password" name="password"><br><br> <input type="submit" value="登陸"> </form> """ return HttpResponse(html) def login_data(request): """驗證登陸""" print( request.POST ) return HttpResponse("登陸成功") """"路由代碼""""" from django.urls.conf import path from . import views urlpatterns = [ path(r"login_form/",views.login_form), path(r"login_data/",views.login_data), ]
類視圖,能夠實現django自動根據用戶的http請求動做,自動識別執行對應名稱的視圖方法。
form django.views import View class MemberView(View): def get(self,request): """獲取用戶數據""" ... def post(self,request): """添加用戶數據""" ... def put(self,request): """修改用戶數據""" ... def delete(self,request): """刪除用戶數據""" ....
代碼:
from django.http import HttpResponse from django.views import View class LoginView(View): def get(self,request): """顯示登陸頁面""" html = """ <form method="post" action="/clsview/login_data/"> 帳號: <input type="text" name="usernane"><br><br> 密碼: <input type="password" name="password"><br><br> <input type="submit" value="登陸"> </form> """ return HttpResponse(html) def post(self,request): """處理登陸驗證""" print( request.POST ) return HttpResponse("登陸成功")
類視圖核心的實現代碼就是as_view和dispatch方法。
類視圖中的視圖方法都是固定名稱,只能是http請求動做的小寫名稱。
drf除了在數據序列化部分簡寫代碼之外,還在視圖中提供了簡寫操做。因此在django原有的django.views.View類基礎上,drf封裝了多個子類出來提供給咱們使用。
Django REST framwork 提供的視圖的主要做用:
爲了方便咱們學習,因此先建立一個子應用req
python manage.py startapp req
REST framework 傳入視圖的request對象再也不是Django默認的HttpRequest對象,而是REST framework提供的擴展了HttpRequest類的Request類的對象。
REST framework 提供了Parser解析器,在接收到請求後會自動根據Content-Type指明的請求數據類型(如JSON、表單等)將請求數據進行parse解析,解析爲類字典[QueryDict]對象保存到Request對象中。
Request對象的數據是自動根據前端發送數據的格式進行解析以後的結果。
不管前端發送的哪一種格式的數據,咱們均可以以統一的方式讀取數據。
request.data
返回解析以後的請求體數據。相似於Django中標準的request.POST
和 request.FILES
屬性,但提供以下特性:
request.query_params
與Django標準的request.GET
相同,只是更換了更正確的名稱而已。
rest_framework.response.Response
REST framework提供了一個響應類Response
,使用該類構造響應對象時,響應的具體數據內容會被轉換(render渲染)成符合前端需求的類型。
REST framework提供了Renderer
渲染器,用來根據請求頭中的Accept
(接收數據類型聲明)來自動轉換響應數據到對應格式。若是前端請求中未進行Accept聲明,則會採用默認方式處理響應數據,咱們能夠經過配置來修改默認響應格式。
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默認響應渲染類 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer', # 瀏覽API渲染器 ) }
Response(data, status=None, template_name=None, headers=None, content_type=None)
data
數據不要是render處理以後的數據,只需傳遞python的內建類型數據便可,REST framework會使用renderer
渲染器處理data
。
data
不能是複雜結構的數據,如Django的模型類對象,對於這樣的數據咱們可使用Serializer
序列化器序列化處理後(轉爲了Python字典類型)再傳遞給data
參數。
參數說明:
data
: 爲響應準備的序列化處理後的數據;status
: 狀態碼,默認200;template_name
: 模板名稱,若是使用HTMLRenderer
時需指明;headers
: 用於存放響應頭信息的字典;content_type
: 響應數據的Content-Type,一般此參數無需傳遞,REST framework會根據前端所需類型數據來設置該參數。傳給response對象的序列化後,但還沒有render處理的數據
狀態碼的數字
通過render處理後的響應數據
爲了方便設置狀態碼,REST framewrok在rest_framework.status
模塊中提供了經常使用狀態碼常量。
HTTP_100_CONTINUE HTTP_101_SWITCHING_PROTOCOLS
HTTP_200_OK HTTP_201_CREATED HTTP_202_ACCEPTED HTTP_203_NON_AUTHORITATIVE_INFORMATION HTTP_204_NO_CONTENT HTTP_205_RESET_CONTENT HTTP_206_PARTIAL_CONTENT HTTP_207_MULTI_STATUS
HTTP_300_MULTIPLE_CHOICES HTTP_301_MOVED_PERMANENTLY HTTP_302_FOUND HTTP_303_SEE_OTHER HTTP_304_NOT_MODIFIED HTTP_305_USE_PROXY HTTP_306_RESERVED HTTP_307_TEMPORARY_REDIRECT
HTTP_400_BAD_REQUEST HTTP_401_UNAUTHORIZED HTTP_402_PAYMENT_REQUIRED HTTP_403_FORBIDDEN HTTP_404_NOT_FOUND HTTP_405_METHOD_NOT_ALLOWED HTTP_406_NOT_ACCEPTABLE HTTP_407_PROXY_AUTHENTICATION_REQUIRED HTTP_408_REQUEST_TIMEOUT HTTP_409_CONFLICT HTTP_410_GONE HTTP_411_LENGTH_REQUIRED HTTP_412_PRECONDITION_FAILED HTTP_413_REQUEST_ENTITY_TOO_LARGE HTTP_414_REQUEST_URI_TOO_LONG HTTP_415_UNSUPPORTED_MEDIA_TYPE HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE HTTP_417_EXPECTATION_FAILED HTTP_422_UNPROCESSABLE_ENTITY HTTP_423_LOCKED HTTP_424_FAILED_DEPENDENCY HTTP_428_PRECONDITION_REQUIRED HTTP_429_TOO_MANY_REQUESTS HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
HTTP_500_INTERNAL_SERVER_ERROR HTTP_501_NOT_IMPLEMENTED HTTP_502_BAD_GATEWAY HTTP_503_SERVICE_UNAVAILABLE HTTP_504_GATEWAY_TIMEOUT HTTP_505_HTTP_VERSION_NOT_SUPPORTED HTTP_507_INSUFFICIENT_STORAGE HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
REST framework 提供了衆多的通用視圖基類與擴展類,以簡化視圖的編寫。
rest_framework.views.APIView
APIView
是REST framework提供的全部視圖的基類,繼承自Django的View
父類。
APIView
與View
的不一樣之處在於:
Request
對象,而不是Django的HttpRequeset
對象;Response
對象,視圖會爲響應數據設置(render)符合前端要求的格式;APIException
異常都會被捕獲到,而且處理成合適的響應信息;支持定義的類屬性
在APIView
中仍以常規的類視圖定義方法來實現get() 、post() 或者其餘請求方式的方法。
舉例:
from rest_framework.views import APIView from rest_framework.response import Response # url(r'^students/$', views.StudentsAPIView.as_view()), class StudentsAPIView(APIView): def get(self, request): data_list = Student.objects.all() serializer = StudentModelSerializer(instance=data_list, many=True) return Response(serializer.data)
rest_framework.generics.GenericAPIView
繼承自APIVIew
,主要增長了操做序列化器和數據庫查詢的方法,做用是爲下面Mixin擴展類的執行提供方法支持。一般在使用時,可搭配一個或多個Mixin擴展類。
提供的關於序列化器使用的屬性與方法
屬性:
方法:
get_serializer_class(self)
當出現一個視圖類中調用多個序列化器時,那麼能夠經過條件判斷在get_serializer_class方法中經過返回不一樣的序列化器類名就可讓視圖方法執行不一樣的序列化器對象了。
返回序列化器類,默認返回serializer_class
,能夠重寫,例如:
def get_serializer_class(self): if self.request.user.is_staff: return FullAccountSerializer return BasicAccountSerializer
返回序列化器對象,主要用來提供給Mixin擴展類使用,若是咱們在視圖中想要獲取序列化器對象,也能夠直接調用此方法。
注意,該方法在提供序列化器對象的時候,會向序列化器對象的context屬性補充三個數據:request、format、view,這三個數據對象能夠在定義序列化器時使用。
提供的關於數據庫查詢的屬性與方法
屬性:
方法:
get_queryset(self)
返回視圖使用的查詢集,主要用來提供給Mixin擴展類使用,是列表視圖與詳情視圖獲取數據的基礎,默認返回queryset
屬性,能夠重寫,例如:
def get_queryset(self): user = self.request.user return user.accounts.all()
get_object(self)
返回詳情視圖所需的模型類數據對象,主要用來提供給Mixin擴展類使用。
在試圖中能夠調用該方法獲取詳情信息的模型類對象。
若詳情訪問的模型類對象不存在,會返回404。
該方法會默認使用APIView提供的check_object_permissions方法檢查當前對象是否有權限被訪問。
舉例:
# url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()), class BookDetailView(GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request, pk): book = self.get_object() # get_object()方法根據pk參數查找queryset中的數據對象 serializer = self.get_serializer(book) return Response(serializer.data)
其餘能夠設置的屬性
爲了方便學習上面的GenericAPIView通用視圖類,咱們新建一個子應用。
python manage.py startapp gen
代碼:
from rest_framework.generics import GenericAPIView from students.models import Student from .serializers import StudentModelSerializer, StudentModel2Serializer from rest_framework.response import Response class StudentsGenericAPIView(GenericAPIView): # 本次視圖類中要操做的數據[必填] queryset = Student.objects.all() # 本次視圖類中要調用的默認序列化器[選填] serializer_class = StudentModelSerializer def get(self, request): """獲取全部學生信息""" serializer = self.get_serializer(instance=self.get_queryset(), many=True) return Response(serializer.data) def post(self,request): data = request.data serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) instance = serializer.save() serializer = self.get_serializer(instance=instance) return Response(serializer.data) class StudentGenericAPIView(GenericAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_serializer_class(self): """重寫獲取序列化器類的方法""" if self.request.method == "GET": return StudentModel2Serializer else: return StudentModelSerializer # 在使用GenericAPIView視圖獲取或操做單個數據時,視圖方法中的表明主鍵的參數最好是pk def get(self,request,pk): """獲取一條數據""" serializer = self.get_serializer(instance=self.get_object()) return Response(serializer.data) def put(self,request,pk): data = request.data serializer = self.get_serializer(instance=self.get_object(),data=data) serializer.is_valid(raise_exception=True) serializer.save() serializer = self.get_serializer(instance=self.get_object()) return Response(serializer.data)
序列化器類:
from rest_framework import serializers from students.models import Student class StudentModelSerializer(serializers.ModelSerializer): class Meta: model= Student fields = "__all__" class StudentModel2Serializer(serializers.ModelSerializer): class Meta: model= Student fields = ("name","class_null")
做用:
提供了幾種後端視圖(對數據資源進行曾刪改查)處理流程的實現,若是須要編寫的視圖屬於這五種,則視圖能夠經過繼承相應的擴展類來複用代碼,減小本身編寫的代碼量。
這五個擴展類須要搭配GenericAPIView父類,由於五個擴展類的實現須要調用GenericAPIView提供的序列化器與數據庫查詢的方法。
列表視圖擴展類,提供list(request, *args, **kwargs)
方法快速實現列表視圖,返回200狀態碼。
該Mixin的list方法會對數據進行過濾和分頁。
源代碼:
class ListModelMixin(object): """ List a queryset. """ def list(self, request, *args, **kwargs): # 過濾 queryset = self.filter_queryset(self.get_queryset()) # 分頁 page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) # 序列化 serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
舉例:
from rest_framework.mixins import ListModelMixin class BookListView(ListModelMixin, GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request): return self.list(request)
建立視圖擴展類,提供create(request, *args, **kwargs)
方法快速實現建立資源的視圖,成功返回201狀態碼。
若是序列化器對前端發送的數據驗證失敗,返回400錯誤。
源代碼:
class CreateModelMixin(object): """ Create a model instance. """ def create(self, request, *args, **kwargs): # 獲取序列化器 serializer = self.get_serializer(data=request.data) # 驗證 serializer.is_valid(raise_exception=True) # 保存 self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer): serializer.save() def get_success_headers(self, data): try: return {'Location': str(data[api_settings.URL_FIELD_NAME])} except (TypeError, KeyError): return {}
詳情視圖擴展類,提供retrieve(request, *args, **kwargs)
方法,能夠快速實現返回一個存在的數據對象。
若是存在,返回200, 不然返回404。
源代碼:
class RetrieveModelMixin(object): """ Retrieve a model instance. """ def retrieve(self, request, *args, **kwargs): # 獲取對象,會檢查對象的權限 instance = self.get_object() # 序列化 serializer = self.get_serializer(instance) return Response(serializer.data)
舉例:
class BookDetailView(RetrieveModelMixin, GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request, pk): return self.retrieve(request)
更新視圖擴展類,提供update(request, *args, **kwargs)
方法,能夠快速實現更新一個存在的數據對象。
同時也提供partial_update(request, *args, **kwargs)
方法,能夠實現局部更新。****
成功返回200,序列化器校驗數據失敗時,返回400錯誤。
源代碼:
class UpdateModelMixin(object): """ Update a model instance. """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} return Response(serializer.data) def perform_update(self, serializer): serializer.save() def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.update(request, *args, **kwargs)
刪除視圖擴展類,提供destroy(request, *args, **kwargs)
方法,能夠快速實現刪除一個存在的數據對象。
成功返回204,不存在返回404。
源代碼:
class DestroyModelMixin(object): """ Destroy a model instance. """ def destroy(self, request, *args, **kwargs): instance = self.get_object() self.perform_destroy(instance) return Response(status=status.HTTP_204_NO_CONTENT) def perform_destroy(self, instance): instance.delete()
使用GenericAPIView和視圖擴展類,實現api接口,代碼:
"""GenericAPIView結合視圖擴展類實現api接口""" from rest_framework.mixins import ListModelMixin,CreateModelMixin class Students2GenericAPIView(GenericAPIView,ListModelMixin,CreateModelMixin): # 本次視圖類中要操做的數據[必填] queryset = Student.objects.all() # 本次視圖類中要調用的默認序列化器[玄天] serializer_class = StudentModelSerializer def get(self, request): """獲取多個學生信息""" return self.list(request) def post(self,request): """添加學生信息""" return self.create(request) from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin class Student2GenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 在使用GenericAPIView視圖獲取或操做單個數據時,視圖方法中的表明主鍵的參數最好是pk def get(self,request,pk): """獲取一條數據""" return self.retrieve(request,pk) def put(self,request,pk): """更新一條數據""" return self.update(request,pk) def delete(self,request,pk): """刪除一條數據""" return self.destroy(request,pk)
提供 post 方法
繼承自: GenericAPIView、CreateModelMixin
2)ListAPIView
提供 get 方法
繼承自:GenericAPIView、ListModelMixin
提供 get 方法
繼承自: GenericAPIView、RetrieveModelMixin
提供 delete 方法
繼承自:GenericAPIView、DestoryModelMixin
提供 put 和 patch 方法
繼承自:GenericAPIView、UpdateModelMixin
提供 get、put、patch方法
繼承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
提供 get、put、patch、delete方法
繼承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
使用視圖集ViewSet,能夠將一系列邏輯相關的動做放到一個類中:
ViewSet視圖集類再也不實現get()、post()等方法,而是實現動做 action 如 list() 、create() 等。
視圖集只在使用as_view()方法的時候,纔會將action動做與具體請求方式對應上。如:
class BookInfoViewSet(viewsets.ViewSet): def list(self, request): books = BookInfo.objects.all() serializer = BookInfoSerializer(books, many=True) return Response(serializer.data) def retrieve(self, request, pk=None): try: books = BookInfo.objects.get(id=pk) except BookInfo.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) serializer = BookInfoSerializer(books) return Response(serializer.data)
在設置路由時,咱們能夠以下操做
urlpatterns = [ url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}), url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'}) ]
繼承自APIView
與ViewSetMixin
,做用也與APIView基本相似,提供了身份認證、權限校驗、流量管理等。
ViewSet主要經過繼承ViewSetMixin來實如今調用as_view()時傳入字典(如{'get':'list'})的映射處理工做。
在ViewSet中,沒有提供任何動做action方法,須要咱們本身實現action方法。
使用ViewSet一般並不方便,由於list、retrieve、create、update、destory等方法都須要本身編寫,而這些方法與前面講過的Mixin擴展類提供的方法同名,因此咱們能夠經過繼承Mixin擴展類來複用這些方法而無需本身編寫。可是Mixin擴展類依賴與GenericAPIView
,因此還須要繼承GenericAPIView
。
GenericViewSet就幫助咱們完成了這樣的繼承工做,繼承自GenericAPIView
與ViewSetMixin
,在實現了調用as_view()時傳入字典(如{'get':'list'}
)的映射處理工做的同時,還提供了GenericAPIView
提供的基礎方法,能夠直接搭配Mixin擴展類使用。
舉例:
from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer
url的定義
urlpatterns = [ path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})), re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})), ]
繼承自GenericViewSet
,同時包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
繼承自GenericViewSet
,同時包括了ListModelMixin、RetrieveModelMixin。
在視圖集中,除了上述默認的方法動做外,還能夠添加自定義動做。
舉例:
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def login(self,request): """學生登陸功能""" return Response({"message":"登陸成功"})
url的定義
urlpatterns = [ path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})), re_path("students8/(?P<pk>\d+)/", views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"})) ]
在視圖集中,咱們能夠經過action對象屬性來獲取當前請求視圖集時的action動做是哪一個。
例如:
from rest_framework.viewsets import ModelViewSet from students.models import Student from .serializers import StudentModelSerializer from rest_framework.response import Response class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_new_5(self,request): """獲取最近添加的5個學生信息""" # 操做數據庫 print(self.action) # 獲取本次請求的視圖方法名 經過路由訪問到當前方法中.能夠看到本次的action就是請求的方法名
對於視圖集ViewSet,咱們除了能夠本身手動指明請求方式與動做action之間的對應關係外,還可使用Routers來幫助咱們快速實現路由信息。
REST framework提供了兩個router
1) 建立router對象,並註冊視圖集,例如
from rest_framework import routers router = routers.DefaultRouter() router.register(r'router_stu', StudentModelViewSet, base_name='student')
register(prefix, viewset, base_name)
如上述代碼會造成的路由以下:
^books/$ name: book-list ^books/{pk}/$ name: book-detail
2)添加路由數據
能夠有兩種方式:
urlpatterns = [ ... ] urlpatterns += router.urls
或
urlpatterns = [ ... url(r'^', include(router.urls)) ]
使用路由類給視圖集生成了路由地址
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def login(self,request): """學生登陸功能""" print(self.action) return Response({"message":"登陸成功"})
路由代碼:
from django.urls import path, re_path from . import views urlpatterns = [ ... ] """使用drf提供路由類router給視圖集生成路由列表""" # 實例化路由類 # drf提供一共提供了兩個路由類給咱們使用,他們用法一致,功能幾乎同樣 from rest_framework.routers import DefaultRouter router = DefaultRouter() # 註冊視圖集 # router.register("路由前綴",視圖集類) router.register("router_stu",views.StudentModelViewSet) # 把生成的路由列表追加到urlpatterns print( router.urls ) urlpatterns += router.urls
上面的代碼就成功生成了路由地址[增/刪/改/查一條/查多條的功能],可是不會自動咱們在視圖集自定義方法的路由。
因此咱們若是也要給自定義方法生成路由,則須要進行action動做的聲明。
在視圖集中,若是想要讓Router自動幫助咱們爲自定義的動做生成路由信息,須要使用rest_framework.decorators.action
裝飾器。
以action裝飾器裝飾的方法名會做爲action動做名,與list、retrieve等同。
action裝飾器能夠接收兩個參數:
methods: 聲明該action對應的請求方式,列表傳遞
detail
: 聲明該action的路徑是否與單一資源對應,及是不是
xxx/<pk>/action方法名/
xxx/<pk>/action方法名/
xxx/action方法名/
舉例:
from rest_framework.viewsets import ModelViewSet from rest_framework.decorators import action class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer # methods 設置當前方法容許哪些http請求訪問當前視圖方法 # detail 設置當前視圖方法是不是操做一個數據 # detail爲True,表示路徑名格式應該爲 router_stu/{pk}/login/ @action(methods=['get'], detail=True) def login(self, request,pk): """登陸""" ... # detail爲False 表示路徑名格式應該爲 router_stu/get_new_5/ @action(methods=['put'], detail=False) def get_new_5(self, request): """獲取最新添加的5個學生信息""" ...
由路由器自動爲此視圖集自定義action方法造成的路由會是以下內容:
^router_stu/get_new_5/$ name: router_stu-get_new_5 ^router_stu/{pk}/login/$ name: router_stu-login
1) SimpleRouter
2)DefaultRouter
DefaultRouter與SimpleRouter的區別是,DefaultRouter會多附帶一個默認的API根視圖,返回一個包含全部列表視圖的超連接響應數據。
能夠在配置文件中配置全局默認的認證方案
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', # session認證 'rest_framework.authentication.BasicAuthentication', # 基本認證 ) }
也能夠在每一個視圖中經過設置authentication_classess屬性來設置
from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.views import APIView class ExampleView(APIView): # 類屬性 authentication_classes = [SessionAuthentication, BasicAuthentication] ...
認證失敗會有兩種可能的返回值:
權限控制能夠限制用戶對於視圖的訪問和對於具體數據對象的訪問。
能夠在配置文件中全局設置默認的權限管理類,如
REST_FRAMEWORK = { .... 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) }
若是未指明,則採用以下默認配置
'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.AllowAny', )
也能夠在具體的視圖中經過permission_classes屬性來設置,如
from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView class ExampleView(APIView): permission_classes = (IsAuthenticated,) ...
舉例
from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.generics import RetrieveAPIView class StudentAPIView(RetrieveAPIView): queryset = Student.objects.all() serializer_class = StudentSerializer authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated]
如需自定義權限,需繼承rest_framework.permissions.BasePermission父類,並實現如下兩個任何一個方法或所有
.has_permission(self, request, view)
是否能夠訪問視圖, view表示當前視圖對象
.has_object_permission(self, request, view, obj)
是否能夠訪問數據對象, view表示當前視圖, obj爲數據對象
例如:
在當前子應用下,建立一個權限文件permissions.py中聲明自定義權限類:
from rest_framework.permissions import BasePermission class IsXiaoMingPermission(BasePermission): def has_permission(self, request, view): if( request.user.username == "xiaoming" ): return True
from .permissions import IsXiaoMingPermission class StudentViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentSerializer permission_classes = [IsXiaoMingPermission]
能夠對接口訪問的頻次進行限制,以減輕服務器壓力。
通常用於付費購買次數,投票等場景使用.
能夠在配置文件中,使用DEFAULT_THROTTLE_CLASSES
和 DEFAULT_THROTTLE_RATES
進行全局配置,
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '1000/day' } }
DEFAULT_THROTTLE_RATES
可使用 second
, minute
, hour
或day
來指明週期。
也能夠在具體視圖中經過throttle_classess屬性來配置,如
from rest_framework.throttling import UserRateThrottle from rest_framework.views import APIView class ExampleView(APIView): throttle_classes = (UserRateThrottle,) ...
1) AnonRateThrottle
限制全部匿名未認證用戶,使用IP區分用戶。
使用DEFAULT_THROTTLE_RATES['anon']
來設置頻次
2)UserRateThrottle
限制認證用戶,使用User id 來區分。
使用DEFAULT_THROTTLE_RATES['user']
來設置頻次
3)ScopedRateThrottle
限制用戶對於每一個視圖的訪問頻次,使用ip或user id。
例如:
class ContactListView(APIView): throttle_scope = 'contacts' ... class ContactDetailView(APIView): throttle_scope = 'contacts' ... class UploadView(APIView): throttle_scope = 'uploads' ... REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.ScopedRateThrottle', ), 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', 'uploads': '20/day' } }
全局配置中設置訪問頻率
'DEFAULT_THROTTLE_RATES': { 'anon': '3/minute', 'user': '10/minute' }
from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.generics import RetrieveAPIView from rest_framework.throttling import UserRateThrottle class StudentAPIView(RetrieveAPIView): queryset = Student.objects.all() serializer_class = StudentSerializer authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated] throttle_classes = (UserRateThrottle,)
對於列表數據可能須要根據字段進行過濾,咱們能夠經過添加django-fitlter擴展來加強支持。
pip install django-filter
在配置文件中增長過濾後端的設置:
INSTALLED_APPS = [ ... 'django_filters', # 須要註冊應用, ] REST_FRAMEWORK = { ... 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
在視圖中添加filter_fields屬性,指定能夠過濾的字段
class StudentListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentSerializer filter_fields = ('age', 'sex') # 127.0.0.1:8000/four/students/?sex=1
對於列表數據,REST framework提供了OrderingFilter過濾器來幫助咱們快速指明數據按照指定字段進行排序。
使用方法:
在類視圖中設置filter_backends,使用rest_framework.filters.OrderingFilter
過濾器,REST framework會在請求的查詢字符串參數中檢查是否包含了ordering參數,若是包含了ordering參數,則按照ordering參數指明的排序字段對數據集進行排序。
前端能夠傳遞的ordering參數的可選字段值須要在ordering_fields中指明。
示例:
class StudentListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_backends = [OrderingFilter] ordering_fields = ('id', 'age') # 127.0.0.1:8000/books/?ordering=-age # -id 表示針對id字段進行倒序排序 # id 表示針對id字段進行升序排序
若是須要在過濾之後再次進行排序,則須要二者結合!
from rest_framework.generics import ListAPIView from students.models import Student from .serializers import StudentModelSerializer from django_filters.rest_framework import DjangoFilterBackend class Student3ListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_fields = ('age', 'sex') # 由於局部配置會覆蓋全局配置,因此須要從新把過濾組件核心類再次聲明, # 不然過濾功能會失效 filter_backends = [OrderingFilter,DjangoFilterBackend] ordering_fields = ('id', 'age')
REST framework提供了分頁的支持。
咱們能夠在配置文件中設置全局的分頁方式,如:
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 100 # 每頁數目 }
也可經過自定義Pagination類,來爲視圖添加不一樣分頁行爲。在視圖中經過pagination_clas
屬性來指明。
class LargeResultsSetPagination(PageNumberPagination): page_size = 1000 page_size_query_param = 'page_size' max_page_size = 10000 class BookDetailView(RetrieveAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer pagination_class = LargeResultsSetPagination
注意:若是在視圖內關閉分頁功能,只需在視圖內設置
pagination_class = None
1) PageNumberPagination
前端訪問網址形式:
GET http://127.0.0.1:8000/students/?page=4
能夠在子類中定義的屬性:
# 聲明分頁的配置類 from rest_framework.pagination import PageNumberPagination class StandardPageNumberPagination(PageNumberPagination): # 默認每一頁顯示的數據量 page_size = 2 # 容許客戶端經過get參數來控制每一頁的數據量 page_size_query_param = "size" max_page_size = 10 # 自定義頁碼的參數名 page_query_param = "p" class StudentAPIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer pagination_class = StandardPageNumberPagination # 127.0.0.1/four/students/?p=1&size=5
2)LimitOffsetPagination
前端訪問網址形式:
GET http://127.0.0.1/four/students/?limit=100&offset=400
能夠在子類中定義的屬性:
PAGE_SIZE
設置一直from rest_framework.pagination import LimitOffsetPagination class StandardLimitOffsetPagination(LimitOffsetPagination): # 默認每一頁查詢的數據量,相似上面的page_size default_limit = 2 limit_query_param = "size" offset_query_param = "start" class StudentAPIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 調用頁碼分頁類 # pagination_class = StandardPageNumberPagination # 調用查詢偏移分頁類 pagination_class = StandardLimitOffsetPagination
REST framework提供了異常處理,咱們能夠自定義異常處理函數。
from rest_framework.views import exception_handler def custom_exception_handler(exc, context): # 先調用REST framework默認的異常處理方法得到標準錯誤響應對象 response = exception_handler(exc, context) # 在此處補充自定義的異常處理 if response is None: response.data['status_code'] = response.status_code return response
在配置文件中聲明自定義的異常處理
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' }
若是未聲明,會採用默認的方式,以下
rest_frame/settings.py
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' }
例如:
補充上處理關於數據庫的異常
from rest_framework.views import exception_handler as drf_exception_handler from rest_framework import status from django.db import DatabaseError def exception_handler(exc, context): response = drf_exception_handler(exc, context) if response is None: view = context['view'] if isinstance(exc, DatabaseError): print('[%s]: %s' % (view, exc)) response = Response({'detail': '服務器內部錯誤'}, status=status.HTTP_507_INSUFFICIENT_STORAGE) return response
也就是說,不少的沒有在上面列出來的異常,就須要咱們在自定義異常中本身處理了。
REST framework能夠自動幫助咱們生成接口文檔。
接口文檔以網頁的方式呈現。
自動接口文檔能生成的是繼承自APIView
及其子類的視圖。
REST framewrok生成接口文檔須要coreapi
庫的支持。
pip install coreapi
在總路由中添加接口文檔路徑。
文檔路由對應的視圖配置爲rest_framework.documentation.include_docs_urls
,
參數title
爲接口文檔網站的標題。
from rest_framework.documentation import include_docs_urls urlpatterns = [ ... path('docs/', include_docs_urls(title='站點頁面標題')) ]
1) 單一方法的視圖,可直接使用類視圖的文檔字符串,如
class BookListView(generics.ListAPIView): """ 返回全部圖書信息. """
2)包含多個方法的視圖,在類視圖的文檔字符串中,分開方法定義,如
class BookListCreateView(generics.ListCreateAPIView): """ get: 返回全部圖書信息. post: 新建圖書. """
3)對於視圖集ViewSet,仍在類視圖的文檔字符串中封開定義,可是應使用action名稱區分,如
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet): """ list: 返回圖書列表數據 retrieve: 返回圖書詳情數據 latest: 返回最新的圖書數據 read: 修改圖書的閱讀量 """
瀏覽器訪問 127.0.0.1:8000/docs/,便可看到自動生成的接口文檔。
1) 視圖集ViewSet中的retrieve名稱,在接口文檔網站中叫作read
2)參數的Description須要在模型類或序列化器類的字段中以help_text選項定義,如:
class Student(models.Model): ... age = models.IntegerField(default=0, verbose_name='年齡', help_text='年齡') ...
或
class StudentSerializer(serializers.ModelSerializer): class Meta: model = Student fields = "__all__" extra_kwargs = { 'age': { 'required': True, 'help_text': '年齡' } }
xadmin是Django的第三方擴展,比使用Django的admin站點更強大也更方便。
文檔:https://xadmin.readthedocs.io/en/latest/index.html
經過以下命令安裝xadmin的最新版
pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2
在配置文件中註冊以下應用
INSTALLED_APPS = [ ... 'xadmin', 'crispy_forms', 'reversion', ... ] # 修改使用中文界面 LANGUAGE_CODE = 'zh-Hans' # 修改時區 TIME_ZONE = 'Asia/Shanghai'
xadmin有創建本身的數據庫模型類,須要進行數據庫遷移
python manage.py makemigrations python manage.py migrate
在總路由中添加xadmin的路由信息
import xadmin xadmin.autodiscover() # version模塊自動註冊須要版本控制的 Model from xadmin.plugins import xversion xversion.register_models() urlpatterns = [ path(r'xadmin/', xadmin.site.urls), ]
若是以前沒有建立超級用戶,須要建立,若是有了,則能夠直接使用以前的。
python manage.py createsuperuser
admin.ModelAdmin
,而是直接繼承object
便可。例如:在子應用中建立adminx.py文件。
import xadmin from xadmin import views class BaseSetting(object): """xadmin的基本配置""" enable_themes = True # 開啓主題切換功能 use_bootswatch = True xadmin.site.register(views.BaseAdminView, BaseSetting) class GlobalSettings(object): """xadmin的全局配置""" site_title = "路飛學城" # 設置站點標題 site_footer = "路飛學城有限公司" # 設置站點的頁腳 menu_style = "accordion" # 設置菜單摺疊 xadmin.site.register(views.CommAdminView, GlobalSettings)
xadmin可使用的頁面樣式控制基本與Django原生的admin一直。
list_display 控制列表展現的字段
list_display = ['id', 'btitle', 'bread', 'bcomment']
search_fields 控制能夠經過搜索框搜索的字段名稱,xadmin使用的是模糊查詢
search_fields = ['id','btitle']
list_filter 能夠進行過濾操做的列,對於分類、性別、狀態
list_filter = ['is_delete']
ordering 默認排序的字段
show_detail_fields 在列表頁提供快速顯示詳情信息
list_editable 在列表頁能夠快速直接編輯的字段
refresh_times 指定列表頁的定時刷新
refresh_times = [5, 10,30,60] # 設置容許後端管理人員按多長時間(秒)刷新頁面
list_export 控制列表頁導出數據的可選格式
list_export = ('xls', 'xml', 'json') list_export設置爲None來禁用數據導出功能 list_export_fields = ('id', 'btitle', 'bpub_date')
show_bookmarks 控制是否顯示書籤功能
show_bookmarks = True
data_charts 控制顯示圖表的樣式
data_charts = { "order_amount": { 'title': '圖書發佈日期表', "x-field": "bpub_date", "y-field": ('btitle',), "order": ('id',) }, # 支持生成多個不一樣的圖表 # "order_amount": { # 'title': '圖書發佈日期表', # "x-field": "bpub_date", # "y-field": ('btitle',), # "order": ('id',) # }, }
model_icon 控制菜單的圖標【圖標的設置能夠參考bootstrap的圖標css名稱】
class BookInfoAdmin(object): model_icon = 'fa fa-gift' xadmin.site.register(models.BookInfo, BookInfodmin)
readonly_fields 在編輯頁面的只讀字段
exclude 在編輯頁面隱藏的字段