1.Request類原始碼分析
# APIView+Response寫個介面
# 總結:
1.新的request有個data屬性,以後只要是在請求body體中的資料,無論什麼編碼格式,無論什麼請求方式
2.取檔案還是從:request.FILES
3.取其他屬性,跟之前完全一樣 request.method ....
原理:新的request重寫了__getattr__,通過反射獲取老的request中的屬性
4.request.GET 現在可以使用request.query_params
@property
def query_params(self):
return self._request.GET
# 補充
魔法方法之__getattr__,物件.屬性 當屬性不存在是,會觸發類中__getattr__的執行
# get請求能不能在body體中帶資料
能!
2.序列化元件介紹
1. 序列化,序列化器會把模型物件(queryset,單個物件)轉換成字典,經過response以後變成json字串
2. 反序列化,把使用者端傳送過來的資料,經過request.data以後變成字典,序列化器可以把字典轉成模型
3. 反序列化,完成資料校驗功能
3.序列化類的基本使用
# 1 建立book表模型
# 2 寫查詢所有圖書的介面:APIVie+序列化類+Response
3.1查詢所有和查詢單條
views.py中
class BookView(APIView):
def get(self,request):
book_list = Book.objects.all()
# 使用序列化類,完成序列化 兩個很重要引數:instance:(範例,物件) data:資料
# 如果是多條many=True 如果是queryset物件 就要寫
# 如果是單個物件many=False 或者可以不寫 預設是False
serializer = BookSerializer(instance=book_list,many=True)
# serializer.data 把queryset物件轉成列表套字典 ReturnList
return Response({'code':100,'msg':'成功','data':serializer.data})
class BookDetailView(APIView):
def get(self,request,pk):
book = Book.objects.get(pk=pk)
serializer = BookSerializer(instance=book,many=False)
return Response({'code':100,'msg':'成功','data':serializer.data})
urls.py中
urlpatterns = [
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookDetailView.as_view()),
]
序列化類
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 要序列化的欄位
id = serializers.IntegerField()
name = serializers.CharField()
price = serializers.IntegerField()
總結
# 序列化類的使用
1.寫一個類 繼承serializers.Serializer
2.在類中寫欄位,要序列化的欄位
3.在檢視類中使用:(多條,單條)
serializer = BookSerializer(instance=book_list, many=True)
serializer = BookSerializer(instance=book)
4.常用欄位類和引數(瞭解)
4.1常用欄位類
欄位 |
欄位構造方式 |
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(maxlength=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=) |
# IntegerField CharField DateTimeField DecimalField
# ListField和DictField---》比較重要,但是後面以案例形式講
4.2欄位引數(校驗資料來用的)
選項引數:
引數名稱 |
作用 |
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頁面時,顯示的欄位幫助提示資訊 |
# read_only write_only 很重要,後面以案例講
5.反序列化之校驗
# 反序列化,有三層校驗
1.欄位自己寫的(寫的欄位引數:required max_length)
2.區域性勾點:寫在序列化類中的方法,方法名必須是 validate_欄位名
def validate_name(self,name):
if 'sb' in name:
# 不合法 拋異常
raise ValidationError('書名中不能包含sb')
else:
return name
3.全域性勾點:寫在序列化類中的方法 方法名必須是 validate
def validate(self,attrs):
price = attrs.get('price')
name = attrs.get('name')
if name == price:
raise VilidationError('價格不能等於書名')
else:
return attrs
# 只有三層都通過 在檢視類中:
ser.is_valid(): 才是True,才能儲存
6.反序列化之儲存
# 新增介面:
序列化類的物件,範例化的時候:ser = BookSerializer(data=request.data)
資料校驗過後--->呼叫 序列化類.save() ---> 但是要在序列化類中重寫 create方法
def create(self,validated_data):
# validated_data校驗過後的資料,字典
book=Book.objects.create(**validated_data)
return book
# 修改介面
序列化類的物件 範例化的時候:ser = BookSerializer(instance=book,data=request.data)
資料校驗過後----》呼叫 序列化類.save()--->但是要在序列化類中重寫 update方法
def update(self,book,validated_data):
for item in validated_data: # {"name":"jinping","price":55}
setattr(book,item,validated_data[item])
# 等同於下面
# setattr(book,'name','jinping')
# setattr(book,'price',55)
# 等同於
# book.name = validated_data.get('name')
# book.price = validated_data.get('price')
book.save()
# 一定不要忘了
return book
# 研究了一個問題
在檢視類中,無論是儲存還是修改,都是呼叫序列化類.save(),底層實現是根據instance做一個判斷 看是否有instance引數傳入
7.增刪改查5個介面
表模型
點選檢視程式碼
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.BigIntegerField()
路由
點選檢視程式碼
urlpatterns = [
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookDetailView.as_view()),
]
檢視
點選檢視程式碼
from .models import Book
from .serialiazer import BookSerialiazer
class BookView(APIView):
# 查所有
def get(self,request):
book_list = Book.objects.all()
# 使用序列化類,完成序列化 兩個很重要的引數:instance(範例,物件) data:資料
# 如果是多條資料 many=True 如果是queryset物件就要寫
# 如果是單個物件 many=False,預設是False
serializer = BookSerializer(instance=book_list,many=True)
# serializer.data # 把queryset物件,轉成列表套字典 ReturnList
return Response({'code':100,'msg':'成功','data':serializer.data})
# 新增
def post(self,request):
# 前端會傳入資料到request.data ---> 把這個資料儲存到資料庫中
# 藉助於序列化類 完成校驗和反序列化
# data前端傳入的資料 {"name":"三國演義","price":88}
ser = BookSerializer(data=request.data)
# 校驗資料
if ser.is_valid(): # 三層:欄位自己的校驗-->區域性勾點校驗-->全域性勾點校驗
# 校驗通過 儲存
print(ser.validated_data) # validated_data:校驗過後的資料
# 如果沒有save,如何儲存,自己做
# Book.objects.create(**validated_data)
ser.save() # 會儲存,但是會報錯 因為它不知道你要儲存到哪個表中
return Response({'code': 100, 'msg': '新增成功'})
else:
print(ser.errors) # 校驗失敗的錯誤
return Response({'code': 101, 'msg': '新增失敗', 'errors': ser.errors})
class BookDetailView(APIView):
# 查詢單條資料
def get(self,request,pk):
book = Book.objects.all().get(pk=pk)
serializer = BookSerializer(instance=book)
return Response({'code': 100, 'msg': '成功', 'data': serializer.data})
def put(self,request,pk):
# 修改單條資料
book = Book.objects.get(pk=pk)
ser = BookSerializer(instance=book,data=request.data)
if ser.is_valid():
ser.save() # 也會報錯 重寫update
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 101, 'msg': '修改失敗', 'errors': ser.errors})
序列化類
點選檢視程式碼
# from rest_framework.serializers import Serializer
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Book
class BookSerializer(serializers.Serializer):
# 要序列化的欄位
id = serializer.IntegerField(required=False) # 前端傳入資料,可以不填這個欄位
name = serializers.CharField(allow_blank=True,required=False,max_length=8,min_length=3,error_messages={'max_length':'太長了'}) # allow_blank:這個欄位傳了,value值可以為空
price = serializers.IntegerField(max_value=100, min_value=10,error_messages={'max_value': '必須小於100'})
'''區域性勾點:給某個欄位做個校驗'''
# 書名中不能包含sb
# validate_欄位名
def validate_name(self,name):
if 'sb' in name:
# 不合法,拋異常
raise ValidationError('書名中不能包含sb')
else:
return name
def validate_price(self,item):
if item == 88:
raise ValidationError('價格不能等於88')
else:
return item
'''全域性勾點'''
# 價格和書名不能一樣 validate
def validate(self,attrs):
price = attrs.get('price')
name = attrs.get('name')
if name == price:
raise ValidationError('價格不能等於書名')
else:
return attrs
def create(self,validated_data):
# validated_data校驗過後的資料 字典
book = Book.objects.create(**validated_data)
return book
def update(self,instance,validated_data):
# instance ---> 要修改的物件
# validated_data ---> 前端傳入 並且校驗過後的資料
for key in validated_data:
setattr(instance,item,validated_data[key])
"""
等同於下面
setattr(instance,'name','jinping')
setattr(instance,'price',55)
等同於下面
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
"""
instance.save() # 一定不要忘記save()
return instance