Django 的DRF框架 Django rest Framework詳解
一、DRF簡單操作:
1、建立序列化器
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書資料序列化器"""
class Meta:
model = BookInfo
fields = '__all__'
- model 指明該序列化器處理的資料欄位從模型類BookInfo參考生成
- fields 指明該序列化器包含模型類中的哪些欄位,'__all__'指明包含所有欄位
2、編寫檢視:
from rest_framework.viewsets import ModelViewSet from .serializers import BookInfoSerializer from .models import BookInfo class BookInfoViewSet(ModelViewSet): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer
- queryset 指明該檢視集在查詢資料時使用的查詢集
- serializer_class 指明該檢視在進行序列化或反序列化時使用的序列化器
3、定義路由
from . import views from rest_framework.routers import DefaultRouter urlpatterns = [ ... ] router = DefaultRouter() # 可以處理檢視的路由器 router.register(r'books', views.BookInfoViewSet) # 向路由器中註冊檢視集 urlpatterns += router.urls # 將路由器中的所以路由資訊追到到django的路由列表中
4、執行測試:
python manage.py runserver
二、定義序列化器Serializer
1、定義方法:
Django REST framework中的Serializer使用類來定義,須繼承rest_framework.serializers.Serializer
1.1建立資料庫模型類BookInfo:
class BookInfo(models.Model): btitle = models.CharField(max_length=20, verbose_name='名稱') bpub_date = models.DateField(verbose_name='釋出日期', null=True) bread = models.IntegerField(default=0, verbose_name='閱讀量') bcomment = models.IntegerField(default=0, verbose_name='評論量') image = models.ImageField(upload_to='booktest', verbose_name='圖片', null=True)
1.2 為模型類提供一個序列化器(寫法一):
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)
1.2.1為模型類提供一個序列化器(寫法二):
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書資料序列化器"""
class Meta:
model = BookInfo
fields = '__all__'
1.3 建立Serializer物件
Serializer(instance=None, data=empty, **kwarg)
說明:
序列化時,將模型類物件傳入instance引數
用於反序列化時,將要被反序列化的資料傳入data引數
除了instance和data引數外,在構造Serializer物件是,還可以通過context引數額外新增資料
serializer = AccountSerializer(account, context={'request': request})
通過content引數附加的資料,可以通過Serializer物件的context屬性獲取。
1.4序列化使用
1.4.1 查詢一個圖書物件
from booktest.models import BookInfo
book = BookInfo.objects.get(id=2)
1.4.2 構造序列化器物件
from booktest.serializers import BookInfoSerializer
serializer = BookInfoSerializer(book)
1.4.3 獲取序列化資料通過data屬性可以獲取序列化後的資料
serializer.data
1.4.4 如果要被序列化的是包含多條資料的查詢集QuerySet,可以通過新增many=True引數補充說明
book_qs = BookInfo.objects.all()
serializer = BookInfoSerializer(book_qs, many=True)
serializer.data
# [OrderedDict([('id', 2), ('btitle', '天龍八部'), ('bpub_date', '1986-07-24'), ('bread', 36), ('bcomment', 40), ('image', N]), OrderedDict([('id', 3), ('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20), ('bcomment', 80), ('image'ne)]), OrderedDict([('id', 4), ('btitle', '雪山飛狐'), ('bpub_date', '1987-11-11'), ('bread', 58), ('bcomment', 24), ('ima None)]), OrderedDict([('id', 5), ('btitle', '西遊記'), ('bpub_date', '1988-01-01'), ('bread', 10), ('bcomment', 10), ('im', 'booktest/xiyouji.png')])]
三、反序列化使用
1、驗證
在獲取反序列化的資料前,必須呼叫is_valid()方法進行驗證,驗證成功返回True,否則返回False。
驗證成功,可以通過序列化器物件的validated_data屬性獲取資料。
驗證失敗,可以通過序列化器物件的errors屬性獲取錯誤資訊,返回字典,包含了欄位和欄位的錯誤。如果是非欄位錯誤,可以通過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯誤字典中的鍵名。
class BookInfoSerializer(serializers.Serializer):
"""圖書資料序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名稱', max_length=20)
bpub_date = serializers.DateField(label='釋出日期', required=False)
bread = serializers.IntegerField(label='閱讀量', required=False)
bcomment = serializers.IntegerField(label='評論量', required=False)
image = serializers.ImageField(label='圖片', required=False)
通過構造序列化器物件,並將要反序列化的資料傳遞給data構造引數,進而進行驗證
from booktest.serializers import BookInfoSerializer
data = {'bpub_date': 123}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # 返回False
serializer.errors
# {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data # {}
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # True
serializer.errors # {}
serializer.validated_data # OrderedDict([('btitle', 'python')])
is_valid()方法還可以在驗證失敗時丟擲異常serializers.ValidationError,可以通過傳遞raise_exception=True引數開啟,REST framework接收到此異常,會向前端返回HTTP 400 Bad Request響應。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
驗證的深入三種方式:
1)validate_<field_name>
對<field_name>
欄位進行驗證,如
class BookInfoSerializer(serializers.Serializer):
"""圖書資料序列化器"""
...
def validate_btitle(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("圖書不是關於Django的")
return value
測試
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}
2)validate在序列化器中需要同時對多個欄位進行比較驗證時,可以定義validate方法來驗證,如
class BookInfoSerializer(serializers.Serializer):
"""圖書資料序列化器"""
...
def validate(self, attrs):
bread = attrs['bread']
bcomment = attrs['bcomment']
if bread < bcomment:
raise serializers.ValidationError('閱讀量小於評論量')
return attrs
測試
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20}
s = BookInfoSerializer(data=data)
s.is_valid() # False
s.errors
# {'non_field_errors': [ErrorDetail(string='閱讀量小於評論量', code='invalid')]}
3)validators在欄位中新增validators選項引數,也可以補充驗證行為,如
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("圖書不是關於Django的")
class BookInfoSerializer(serializers.Serializer):
"""圖書資料序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名稱', max_length=20, validators=[about_django])
bpub_date = serializers.DateField(label='釋出日期', required=False)
bread = serializers.IntegerField(label='閱讀量', required=False)
bcomment = serializers.IntegerField(label='評論量', required=False)
image = serializers.ImageField(label='圖片', required=False)
測試:
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}
2、儲存
驗證成功後,如果需要在返回資料物件的時候,也將資料儲存到資料庫中,則可以進行如下修改
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引數獲取到
serializer.save(owner=request.user)
四、模型類序列化器ModelSerializer
ModelSerializer與常規的Serializer相同,但提供了:
- 基於模型類自動生成一系列欄位
- 基於模型類自動為Serializer生成validators,比如unique_together
- 包含預設的create()和update()的實現
1、定義
我們建立一個BookInfoSerializer
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書資料序列化器"""
class Meta:
model = BookInfo
fields = '__all__'
- model 指明參照哪個模型類
- fields 指明為模型類的哪些欄位生成
2、新增額外欄位
我們可以使用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': {'max_value': 0, 'required': True}},
}
五、Request與Response
1、request
常用屬性:
1).request.data
返回解析之後的請求體資料。類似於Django中標準的request.POST
和request.FILES
屬性,但提供如下特性:
- 包含了解析之後的檔案和非檔案資料
- 包含了對POST、PUT、PATCH請求方式解析後的資料
- 利用了REST framework的parsers解析器,不僅支援表單型別資料,也支援JSON資料
2).query_params
request.query_params
與Django標準的request.GET
相同,只是更換了更正確的名稱而已。
2、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
: 為響應準備的序列化處理後的資料;status
: 狀態碼,預設200;template_name
: 模板名稱,如果使用HTMLRenderer
時需指明;headers
: 用於存放響應頭資訊的字典;content_type
: 響應資料的Content-Type,通常此引數無需傳遞,REST framework會根據前端所需型別資料來設定該引數。
1).data
傳給response物件的序列化後,但尚未render處理的資料
2).status_code
狀態碼的數字
3).content
經過render處理後的響應資料
六、檢視基類
1、兩個基類
1)APIView
在APIView
中仍以常規的類檢視定義方法來實現get() 、post() 或者其他請求方式的方法。
from rest_framework.views import APIView
from rest_framework.response import Response
# url(r'^books/$', views.BookListView.as_view()),
class BookListView(APIView):
def get(self, request):
books = BookInfo.objects.all()
serializer = BookInfoSerializer(books, many=True)
return Response(serializer.data)
2)GenericAPIView
繼承APIView
- 列表檢視與詳情檢視通用:
- queryset 列表檢視的查詢集
- serializer_class 檢視使用的序列化器
- 列表檢視使用:
- pagination_class 分頁控制類
- filter_backends 過濾控制後端
- 詳情頁檢視使用:
- lookup_field 查詢單一資料庫物件時使用的條件欄位,預設為'
pk
' - lookup_url_kwarg 查詢單一資料時URL中的引數關鍵字名稱,預設與look_field相同
- lookup_field 查詢單一資料庫物件時使用的條件欄位,預設為'
get_object(self) 返回詳情檢視所需的模型類資料物件,預設使用lookup_field
引數來過濾queryset。 在試圖中可以呼叫該方法獲取詳情資訊的模型類物件。
舉例:
# 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()
serializer = self.get_serializer(book)
return Response(serializer.data)