Django框架之drf
一、反序列化類校驗部分原始碼解析
反序列化校驗什麼時候開始執行校驗?
剖析流程一:在檢視中使用反序列化器例項化的物件呼叫is_valid()的時候就會進行校驗,通販校驗返回True,反之False
class BookView(APIView):
# 新增
def post(self, request):
ser_obj = BookSerializer(data=request.data)
# 觸發反序列化類校驗
if ser_obj.is_valid():
ser_obj.save()
return Response({'code': 100, 'msg': '新增圖書成功', 'result': ser_obj.data})
return Response({'code': 101, 'msg': ser_obj.errors})
解析流程二:入口:ser.is_valid() 是序列化類的物件,假設序列化類是BookSerializer---》is_valid---》找不到,在父類中找到BaseSerializer
def is_valid(self, *, raise_exception=False):
if not hasattr(self, '_validated_data'):
try:
# self序列化類的物件,屬性中沒有_validated_data,一定會走這句【核心】
self._validated_data = self.run_validation(self.initial_data)
except ValidationError as exc:
self._validated_data = {}
self._errors = exc.detail
else:
self._errors = {}
if self._errors and raise_exception:
raise ValidationError(self.errors)
return not bool(self._errors)
解析流程三:self._validated_data = self.run_validation(self.initial_data) 核心--》self序列化類的物件
-
切記一定不要按住ctrl鍵點選
-
真正的執行順序是,從下往上找,找不到,再往上
-
最終從Serializer類中找到了run_validation,而不是Field中的run_validation
def run_validation(self, data=empty):
# 欄位自己的,validates方法
(is_empty_value, data) = self.validate_empty_values(data)
if is_empty_value:
return data
# 區域性鉤子----【區域性鉤子】
value = self.to_internal_value(data)
try:
self.run_validators(value)
# 全域性鉤子--》如果在BookSerializer中寫了validate,優先走它,非常簡單
value = self.validate(value)
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=as_serializer_error(exc))
return value
# 區域性鉤子 self.to_internal_value(data)---》self是BookSerializer的物件,從根上找
def to_internal_value(self, data):
ret = OrderedDict()
errors = OrderedDict()
fields = self._writable_fields
# fields寫在序列化類中一個個欄位類的物件
for field in fields:
# self BookSerializer的物件,反射validate_name
validate_method = getattr(self, 'validate_' + field.field_name, None)
try:
# 在執行BookSerializer類中的validate_name方法,傳入了要校驗的資料
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
二、斷言
用於判斷一個表示式,在表示式的條件為False時會觸發異常,反之正常往下執行程式碼,斷言可以在條件不滿足的時候主動丟擲異常
關鍵詞:assert
用法:
name = 'kangkang'
# 如果name不等於'kangkang',那麼將會直接丟擲異常程式碼
assert name== 'kangkang'
print('正確,正常執行')
三、drf之請求
1、Request能夠解析的前端傳入編碼格式
drf為我們提供了三種編碼格式分別是:
-
第一種:rest_framework.parsers.JSONParser
- 對應前端的:json格式資料
-
第二種:rest_framework.parsers.FormParser
- 對應前端的:form-data格式資料
-
第三種:rest_framework.parsers.MultiPartParser
- 對應前端的:urlencode
配置請求編碼的方法:
這三種編碼在預設都是開啟的,配置的方法有兩種:
-
在檢視類中配置(區域性)
-
django的settings.py檔案中配置(全域性)
匯入模組:(三種請求模組)
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
# 方式一:在檢視類中配置(只會影響該檢視類下的方法)
class BookView(APIView):
parser_classes = ['JSONParser', 'FormParser', 'MultiPartParser']
# 方式二:django的settings.py中配置(影響全域性)
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
],
}
# 方式三:全域性配了1個,某個檢視類想要3個,怎麼配?
-只需要在檢視類,配置3個即可
-因為:先從檢視類自身找,找不到,去專案的drf配置中找,再找不到,去drf預設的配置找
2、Request類中的屬性和方法
# 該節在上述中已經講過,此處只做總結
繼承APIView的檢視類中的request是經過drf處理後的requset,該類不僅包含了django原生的request的所有方法,還新增了以下功能
1、request.data # (所有請求資料都可以在這裡取出,升級版的request.body)
2、__getattr__ # 該魔法方法是新的request中的方法,呼叫不存在的方法會回到老的requert中去找
3、query_params # 和老的request.get用法一致
四、drf之響應
1、Response能夠響應的編碼格式
drf是django的一個app,所以需要在django的settings.py中進行配置,否則瀏覽器將無法正常訪問
drf的響應,使用瀏覽器和postman訪問同一個介面,返回的格式是不一樣的,如果是瀏覽器將會返回的格式好看一點,postman直接返回資料本身。響應的格式共有兩種,配置方法類似於請求的配置方法
配置方法:
匯入模組:(兩種響應方式模組)
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
# 方式一:檢視類中配置(區域性配置,不會影響全域性)
class BookView(APIView):
renderer_classes=[JSONRenderer,]
# 方式二、django中配置專案檔案(全域性配置,區域性配置的話優先採用區域性)
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
}
# 方式三:使用順序(一般就用內建的即可)
優先使用檢視類中的配置,其次使用專案配置檔案中的配置,最後使用內建的
2、Response的原始碼屬性或方法
Response原始碼分析
# 匯入Response模組:
from rest_framework.response import Response
# Response init可以傳的引數
def __init__(self,
data=None,
status=None,
template_name=None,
headers=None,
exception=False,
content_type=None)
# data:
之前我們們寫的ser.data,可以是字典或列表,字串---》序列化後返回給前端---》前端在響應體中看到的就是這個
# status:
http響應的狀態碼,預設是200,可以透過匯入模組進行修改
-from rest_framework.status import HTTP_200_OK
-Response('xxx',status=status.HTTP_200_OK)
# template_name:
解即可,修改響應模板的樣子,BrowsableAPIRenderer定死的樣子,後期公司可以自己定製
# headers:
響應頭,http響應的響應頭
原生django,響應頭中新增資料
obj = HttpResponse('dddd')
obj['xxc'] = 'yyc'
return obj
# content_type :
響應編碼格式,一般不動
五、檢視元件介紹及兩個檢視基類
APIView是drf提供的檢視類中最頂層的檢視基類
1、APIView與View區別
- 傳入到檢視方法中的是REST framework的Request物件,而不是Django的HttpRequeset物件
- 檢視方法可以返回REST framework的Response物件
- 任何APIException異常都會被捕獲到,並且處理成合適的響應資訊
- 在進行dispatch()分發前,會對請求進行身份認證、許可權檢查、流量控制(執行三大認證)
2、檢視基類
APIVIew
-類屬性:
renderer_classes # 響應格式
parser_classes # 能夠解析的請求格式
authentication_classes #認證類
throttle_classes # 頻率類
permission_classes # 許可權類
六、基於APIView+ModelSerializer+Resposne寫5個介面
1、檢視類
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
from .models import Book
class BookView(APIView):
# 查詢多條
def get(self, request):
book_obj_all = Book.objects.all()
serializer_obj = BookSerializer(instance=book_obj_all, many=True)
return Response(serializer_obj.data)
# 新增
def post(self, request):
res_obj = BookSerializer(data=request.data)
res_obj.is_valid(raise_exception=True)
res_obj.save()
return Response({'cede': 100, 'msg': '新增成功', 'result': res_obj.data})
class BookDetailView(APIView):
# 查詢單條
def get(self, request, *args, **kwargs):
book_obj = Book.objects.filter(**kwargs).first()
serializer_obj = BookSerializer(instance=book_obj)
return Response(serializer_obj.data)
# 修改
def put(self, request, *args, **kwargs):
target_book_obj = Book.objects.filter(**kwargs).first()
serializer_obj = BookSerializer(instance=target_book_obj, data=request.data)
serializer_obj.is_valid(raise_exception=True)
serializer_obj.save()
return Response({'code': 100, 'msg': '修改成功', 'result': serializer_obj.data})
# 刪除
def delete(self, request, *args, **kwargs):
Book.objects.filter(**kwargs).delete()
return Response({'code': 100, 'msg': '刪除成功'})
2、序列化類
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Book
# 序列化類
class BookSerializer(serializers.ModelSerializer):
class Meta:
# 繫結的表
model = Book
# 序列化的欄位
fields = ['pk', 'name', 'price', 'authorList', 'publishDetail', 'author', 'publish']
# 欄位校驗條件
extra_kwargs = {
'name': {'max_length': 32},
'authorList': {'read_only': True},
'publishDetail': {'read_only': True},
'author': {'write_only': True},
'publish': {'write_only': True}
}
# 全域性鉤子
def validate(self, attrs):
if int(attrs.get('price')) > 100:
raise ValidationError('價格不能超過100元')
return attrs
3、路由
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 查詢多條、新增介面
path('book/', views.BookView.as_view()),
# 查詢單條、修改、刪除介面
path('book/<int:pk>/', views.BookDetailView.as_view())
]
4、模型類
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
author = models.ManyToManyField(to='Author')
# 外來鍵欄位資料序列化功能函式
def authorList(self):
author_data_list = [{'name': author_obj.name,
'phone': author_obj.phone,
'age': author_obj.authorinfo.age,
'address': author_obj.authorinfo.gender
} for author_obj in self.author.all()]
return author_data_list
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
# 外來鍵欄位資料序列化功能函式
def publishDetail(self):
return {'name': self.publish.name, 'address': self.publish.address}
class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11)
authorinfo = models.OneToOneField(to='AuthorInfo', on_delete=models.CASCADE, null=True)
class AuthorInfo(models.Model):
age = models.CharField(max_length=8)
gender = models.CharField(max_length=8)
class Publish(models.Model):
name = models.CharField(max_length=32)
address = models.CharField(max_length=32)
七、基於GenericAPIView+5個檢視擴充套件類
1、檢視類
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
RetrieveModelMixin
from .serializer import BookSerializer
from .models import Book
class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 查詢多條
def get(self, request):
return self.list(request)
# 新增
def post(self, request):
return self.create(request)
class BookDetailView(GenericAPIView,
RetrieveModelMixin,
DestroyModelMixin,
UpdateModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 查詢單條
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
# 修改
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
# 刪除
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
2、序列化類
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Book
# 序列化類
class BookSerializer(serializers.ModelSerializer):
class Meta:
# 繫結的表
model = Book
# 序列化的欄位
fields = ['pk', 'name', 'price', 'authorList', 'publishDetail', 'author', 'publish']
# 欄位校驗條件
extra_kwargs = {
'name': {'max_length': 32},
'authorList': {'read_only': True},
'publishDetail': {'read_only': True},
'author': {'write_only': True},
'publish': {'write_only': True}
}
# 全域性鉤子
def validate(self, attrs):
if int(attrs.get('price')) > 100:
raise ValidationError('價格不能超過100元')
return attrs
3、路由
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 查詢多條、新增介面
path('book/', views.BookView.as_view()),
# 查詢單條、修改、刪除介面
path('book/<int:pk>/', views.BookDetailView.as_view())
]
4、模型類
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
author = models.ManyToManyField(to='Author')
# 外來鍵欄位資料序列化功能函式
def authorList(self):
author_data_list = [{'name': author_obj.name,
'phone': author_obj.phone,
'age': author_obj.authorinfo.age,
'address': author_obj.authorinfo.gender
} for author_obj in self.author.all()]
return author_data_list
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
# 外來鍵欄位資料序列化功能函式
def publishDetail(self):
return {'name': self.publish.name, 'address': self.publish.address}
class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11)
authorinfo = models.OneToOneField(to='AuthorInfo', on_delete=models.CASCADE, null=True)
class AuthorInfo(models.Model):
age = models.CharField(max_length=8)
gender = models.CharField(max_length=8)
class Publish(models.Model):
name = models.CharField(max_length=32)
address = models.CharField(max_length=32)