drf序列化元件
阿新 • • 發佈:2020-07-27
一. 序列化元件介紹#
Copy1. 序列化,序列化器會把模型物件轉換成字典,經過response以後變成json字串
2. 反序列化,把客戶端傳送過來的資料,經過request以後變成字典,序列化器可以把字典轉成模型
3. 反序列化,完成資料校驗功能
二. 簡單使用#
步驟:
Copy1. 寫一個序列化的類,繼承Serializer
2. 在類中寫要序列化的欄位,想序列化哪個欄位,就在類中寫哪個欄位
3. 在檢視類中使用,匯入序列化類把要序列化的物件傳入, 得到序列化物件
4. 可以通過'序列化類的物件.data'獲取序列化後的字典
5. 把字典返回. 提示: 如果不使用rest_framework提供的Response,就得使用JsonResponse
程式碼:
Copy# models.py
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='書名')
price = models.CharField(max_length=64, verbose_name='價格')
author = models.CharField(max_length=64, verbose_name='作者')
publish = models.CharField(max_length=64, verbose_name='出版社' )
# ser.py
class BookSerializer(serializers.Serializer):
# id=serializers.CharField()
name = serializers.CharField()
# price=serializers.DecimalField()
price = serializers.CharField()
author = serializers.CharField()
publish = serializers.CharField()
# views.py
class BookView (APIView):
def get(self, request, pk):
book = Book.objects.filter(id=pk).first()
# 將從資料庫中獲取的book物件使用剛剛自定義的序列化類BookSerializer將book進行序列化
book_ser = BookSerializer(book) # 呼叫類的__init__
# book_ser.data # 序列化物件.data就是序列化後的字典
return Response(book_ser.data)
# urls.py
re_path('books/(?P<pk>\d+)', views.BookView.as_view())
三. 序列化類的欄位型別#
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=) |
2. 選項引數#
引數名稱 | 作用 |
---|---|
max_length | 最大長度 |
min_lenght | 最小長度 |
allow_blank | 是否允許為空 |
trim_whitespace | 是否截斷空白字元 |
max_value | 最小值 |
min_value | 最大值 |
3. 通用引數#
引數名稱 | 說明 |
---|---|
read_only | 表明該欄位僅用於序列化輸出,預設False |
write_only | 表明該欄位僅用於反序列化輸入,預設False |
required | 表明該欄位在反序列化時必須輸入,預設True |
default | 反序列化時使用的預設值 |
allow_null | 表明該欄位是否允許傳入None,預設False |
validators | 該欄位使用的驗證器 |
error_messages | 包含錯誤編號與錯誤資訊的字典 |
label | 用於HTML展示API頁面時,顯示的欄位名稱 |
help_text | 用於HTML展示API頁面時,顯示的欄位幫助提示資訊 |
四. 序列化元件修改資料#
步驟:
Copy1. 寫一個序列化的類,繼承Serializer
2. 在類中寫要反序列化的欄位,想反序列化哪個欄位,就在類中寫哪個欄位,欄位的屬性
max_length 最大長度
min_lenght 最小長度
allow_blank 是否允許為空
trim_whitespace 是否截斷空白字元
max_value 最小值
min_value 最大值
3. 在檢視類中使用,匯入自定義的繼承繼承Serializer類的序列化類,把要要修改的物件傳入,得到序列序列化物件.
boo_ser=BookSerializer(book,request.data)
boo_ser=BookSerializer(instance=book,data=request.data)
4. 資料校驗 if boo_ser.is_valid()
5. 如果校驗通過,就儲存
boo_ser.save() # 注意不是book.save()
6. 如果不通過,返回restful規範的返回結果
7. 如果欄位的校驗規則不夠,可以寫鉤子函式(區域性和全域性)
# 區域性鉤子
def validate_price(self, data): # validate_欄位名 接收一個引數
# 如果價格小於10,就校驗不通過
# print(type(data))
# print(data)
if float(data) > 10:
return data
else:
# 校驗失敗,拋異常
raise ValidationError('價格太低')
# 全域性鉤子
def validate(self, validate_data): # 全域性鉤子
# print(validate_data)
author = validate_data.get('author')
publish = validate_data.get('publish')
if author == publish:
raise ValidationError('作者名字跟出版社一樣')
else:
return validate_data
8.也可以可以使用欄位的author = serializers.CharField(validators=[check_author]) ,來校驗
# 定義一個校驗函式
def check_author(data):
if data.startswith('sb'):
raise ValidationError('作者名字不能以sb開頭')
else:
return data
# 在序列化欄位中配置引數:validators=[check_author]
程式碼:
Copy# models.py
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
author = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
# ser.py
# from rest_framework.serializers import Serializer # Serializer就是一個序列化類
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
def check_author(data):
if data.startswith('sb'):
raise ValidationError('作者名字不能以sb開頭')
else:
return data
class BookSerializer(serializers.Serializer):
# id=serializers.CharField()
name = serializers.CharField(max_length=16, min_length=4)
# price=serializers.DecimalField()
price = serializers.CharField()
author = serializers.CharField(validators=[check_author]) # validators=[] 列表中寫函式記憶體地址
publish = serializers.CharField()
def validate_price(self, data): # validate_欄位名 接收一個引數
# 如果價格小於10,就校驗不通過
# print(type(data))
# print(data)
if float(data) > 10:
return data
else:
# 校驗失敗,拋異常
raise ValidationError('價格太低')
def validate(self, validate_data): # 全域性鉤子
print(validate_data)
author = validate_data.get('author')
publish = validate_data.get('publish')
if author == publish:
raise ValidationError('作者名字跟出版社一樣')
else:
return validate_data
def update(self, instance, validated_data):
# instance是book這個物件
# validated_data是校驗後的資料
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.author = validated_data.get('author')
instance.publish = validated_data.get('publish')
instance.save() # book.save() django 的orm提供的
return instance
# views.py
class BookView(APIView):
def get(self, request, pk):
book = Book.objects.filter(id=pk).first()
# 用一個類,毫無疑問,一定要例項化
# 要序列化誰,就把誰傳過來
book_ser = BookSerializer(book) # 呼叫類的__init__
# book_ser.data 序列化物件.data就是序列化後的字典
return Response(book_ser.data)
# return JsonResponse(book_ser.data)
def put(self, request, pk):
response_msg = {'status': 100, 'msg': '成功'}
# 找到這個物件
book = Book.objects.filter(id=pk).first()
# 得到一個序列化類的物件
# boo_ser=BookSerializer(book,request.data)
boo_ser = BookSerializer(instance=book, data=request.data)
# 要資料驗證(回想form表單的驗證)
if boo_ser.is_valid(): # 返回True表示驗證通過
boo_ser.save() # 報錯
response_msg['data'] = boo_ser.data
else:
response_msg['status'] = 101
response_msg['msg'] = '資料校驗失敗'
response_msg['data'] = boo_ser.errors
return Response(response_msg)
# urls.py
re_path('books/(?P<pk>\d+)', views.BookView.as_view()),
五. read_only和write_only#
Copy# 常用
read_only 表明該欄位僅用於序列化輸出,預設False,如果設定成True,postman中可以看到該欄位,修改時,不需要傳該欄位
write_only 表明該欄位僅用於反序列化輸入,預設False,如果設定成True,postman中看不到該欄位,修改時,該欄位需要傳
# 瞭解
required 表明該欄位在反序列化時必須輸入,預設True
default 反序列化時使用的預設值
allow_null 表明該欄位是否允許傳入None,預設False
validators 該欄位使用的驗證器
error_messages 包含錯誤編號與錯誤資訊的字典
六. 查詢所有#
Copy# views.py
class BooksView(APIView):
def get(self, request):
response_msg = {'status': 100, 'msg': '成功'}
books = Book.objects.all()
book_ser = BookSerializer(books, many=True) # 序列化多條,如果序列化一條,不需要寫
response_msg['data'] = book_ser.data
return Response(response_msg)
# urls.py
path('books/', views.BooksView.as_view())
七. 新增資料#
Copy# views.py
class BooksView(APIView):
# 新增
def post(self,request):
response_msg = {'status': 100, 'msg': '成功'}
#修改才有instance,新增沒有instance,只有data
book_ser = BookSerializer(data=request.data)
# book_ser = BookSerializer(request.data) # 這個按位置傳request.data會給instance,就報錯了
# 校驗欄位
if book_ser.is_valid():
book_ser.save()
response_msg['data']=book_ser.data
else:
response_msg['status']=102
response_msg['msg']='資料校驗失敗'
response_msg['data']=book_ser.errors
return Response(response_msg)
#ser.py 序列化類重寫create方法
def create(self, validated_data):
instance=Book.objects.create(**validated_data)
return instance
# urls.py
path('books/', views.BooksView.as_view()),
八. 刪除一個數據#
Copy# views.py
class BookView(APIView):
def delete(self,request,pk):
ret=Book.objects.filter(pk=pk).delete()
return Response({'status':100,'msg':'刪除成功'})
# urls.py
re_path('books/(?P<pk>\d+)', views.BookView.as_view()),
九. 模型類序列化器#
Copyclass BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 對應上models.py中的模型
fields = '__all__'
# fields=('name','price','id','author') # 只序列化指定的欄位
# exclude=('name',) # 跟fields不能都寫,寫誰,就表示排除誰
# read_only_fields=('price',)
# write_only_fields=('id',) # 棄用了,使用extra_kwargs
extra_kwargs = { # 類似於這種形式name=serializers.CharField(max_length=16,min_length=4)
'price': {'write_only': True},
}
# 其他使用一模一樣
# 不需要重寫create和updata方法了
十. 原始碼分析#
1. many=True#
Copy# 序列化多條,需要傳many=True
book_ser=BookModelSerializer(books,many=True)
book_one_ser=BookModelSerializer(book)
print(type(book_ser))
# <class 'rest_framework.serializers.ListSerializer'>
print(type(book_one_ser))
# <class 'app01.ser.BookModelSerializer'>
# 物件的生成--》先呼叫類的__new__方法,生成空物件
# 物件=類名(name=lqz),觸發類的__init__()
# 類的__new__方法控制物件的生成
def __new__(cls, *args, **kwargs):
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
# 沒有傳many=True,走下面,正常的物件例項化
return super().__new__(cls, *args, **kwargs)
2. Serializer高階用法#
1). source#
Copy# 1. 可以改欄位名字
xxx = serializers.CharField(source='title')
# 2. 可以.跨表
publish = serializers.CharField(source='publish.email')
# 3. 可以執行方法
pub_date= serializers.CharField(source='test') # 注: test是Book表模型中的方法
2). SerializerMethodField#
Copyauthors=serializers.SerializerMethodField() # 它需要有個配套方法,方法名叫get_欄位名,返回值就是要顯示的東西
def get_authors(self,instance):
# book物件
authors=instance.authors.all() # 取出所有作者
ll=[]
for author in authors:
ll.append({'name':author.name,'age':author.age})
return ll
十一. 補充: is_valid的另一種用法#
is_valid()方法還可以在驗證失敗時丟擲異常serializers.ValidationError,可以通過傳遞raise_exception = True引數開啟,REST框架接收到此異常,會向前端返回HTTP 400錯誤的請求響應。
Copy# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)