[Django REST framework - 序列化元件、source、鉤子函式]
序列化器-Serializer
什麼是rest_framework序列化?
在寫前後端不分離的專案時:
我們有form元件幫我們去做資料校驗
我們有模板語法,從資料庫取出的queryset物件不需要人為去轉格式
當我們寫前後端分離專案的時:
我們需要自己去做資料校驗
我們需要手動去轉資料格式,因為跨平臺資料傳輸都用json字串,不能直接jsonqueryset物件
序列化器的作用
1 序列化:把python中的物件轉成json格式字串
序列化器會把模型物件轉換成字典,經過response以後變成json字串
2 反序列化:把json格式字串轉成python中的物件
把客戶端傳送過來的資料,經過request以後變成字典,序列化器可以把字典轉成模型
3 注意:drf的序列化元件(序列化器)
把物件(Book,queryset物件)轉成字典
因為有字典,直接丟到Response中就可以了
序列化,反序列化
-物件----》json
-json---》物件
序列化器
-定義一個類,繼承Serializer
-在類內些欄位(常用欄位,和非常用欄位)(欄位引數)
-在檢視類中,例項化得到一個序列化類的物件,傳入要序列化的資料
-物件.data---》就是字典
-source
序列化器的基本使用
from rest_framework.serializers import Serializer,ModelSerializer
from rest_framework import serializers
Serializer是rest_framework原生的序列化元件
ModelSerializer是rest_framework在原生的序列化元件的基礎上封裝了一層的序列化元件
用法:1、在用我們的rest_framework序列化元件的時候,我們的檢視層都必須寫檢視類,不能再寫檢視函式
2、我們需要針對每一張模型表寫一個類來繼承Serailizer或者ModelSerailizer類,
注:當我們在檢視類裡需要對資料進行序列化或者反序列化的時候,在自己定義的類傳入需要序列化的資料例項化,
呼叫.data即可拿到序列化或者校驗後的資料了
Django REST framework中的Serializer使用類來定義,須繼承自rest_framework.serializers.Serializer
1 寫一個序列化的類,繼承Serializer
class BookSerializer(serializers.Serializer):
# 在這裡寫要序列化的欄位
# 序列化欄位類(有很多,常用的就幾個,等同於models中的欄位類)
# 欄位類,有很多欄位引數()
name = serializers.CharField()
price = serializers.IntegerField()
# publish = serializers.CharField()
2 在類中寫要序列化的欄位(常用欄位,和非常用欄位)(欄位引數)
name = serializers.CharField()
price = serializers.IntegerField()
3 在檢視類中使用(例項化得到一個序列化類的物件,傳入要序列化的資料)
class BookView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
# instance=None, 要序列化的資料
# data=empty ,要反序列化的資料(目前data先不用)
# many=True 如果序列化多條,一定要寫many=True
book_ser = BookSerializer(instance=book_list, many=True)
# book_ser.data就是序列化後的資料
return Response(book_ser.data)
4 得到序列化後的資料,返回(物件.data---》就是字典)
book_ser.data
5 欄位引數,source,指定要序列化表中的哪個欄位
##########具體操作步驟見下方詳細操作#########
路由層 urls.py
from django.urls import path
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
# 獲取所有的書籍資訊
path('books_new/', views.BookView.as_view()),
# 對某本書進行操作
path('books_new/<int:id>/', views.BookViewId.as_view()),
]
模型層 models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='書名')
publish = models.CharField(max_length=32, verbose_name='出版社')
price = models.IntegerField(verbose_name='價格')
序列化器層 serializer.py
from rest_framework import serializers
from app import models
# 新建序列化類,繼承Serializer
class BookSerializer(serializers.Serializer):
# 類中定義和模型表一一對應的欄位,在這裡寫要序列化的欄位
# 序列化欄位類(有很多,常用的就幾個,等同於models中的欄位類)
# 欄位類,有很多欄位引數()
name = serializers.CharField()
price = serializers.IntegerField()
publish = serializers.CharField()
檢視層 views.py
# 獲取所有書籍資訊
class BookView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
book_ser = BookSerializer(instance=book_list, many=True)
# book_ser.data就是序列化後的資料
return Response(book_ser.data)
"""
instance=None, 要序列化的資料
data=empty ,要反序列化的資料(目前data先不用)
many=True 如果序列化多條,一定要寫many=True
"""
# 對某一本書進行操作
class BookViewId(APIView):
# 對某一個進行操作需要帶上id
def get(self, request, id):
book = models.Book.objects.all().filter(pk=id).first()
book_ser = BookSerializer(instance=book)
return Response(book_ser.data)
source
1 指定要序列化的欄位(資料表中欄位)
publish = serializers.CharField(source='publish.city') # 拿到出版社所在的城市
2 用的最多:只有一個欄位(也可以跨表)
SerializerMethodField
用的最多:跨表查((來定製返回的欄位)要麼是列表,要麼是字典)
publish=serializers.SerializerMethodField()
def get_publish(self,obj):
print(obj)
# return {'name':'sss','city':'sss'}
return {'name':obj.publish.name,'city':obj.publish.city,'email': obj.publish.email}
在模型表中寫方法
# models.py 表模型中寫的
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField()
publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
authors=models.ManyToManyField(to='Author')
def __str__(self):
return self.name
# def publish_name(self):
# return self.publish.name
def publish_name(self):
return {'name':self.publish.name,'city':self.publish.city}
@property
def author_list(self): # 列表推導式
return [{'name':author.name,'age':author.age,'id':author.nid} for author in self.authors.all()]
# serializers.py 序列化類中
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.IntegerField()
publish_name = serializers.DictField()
author_list = serializers.ListField()
注意:serializer不是隻能為資料庫模型類定義,也可以為非資料庫模型類的資料定義。serializer是獨立於資料庫之外的存在。
序列化類常用欄位型別及屬性
欄位引數針對性分類
# 針對charfield
max_length 最大長度
min_lenght 最小長度
allow_blank 是否允許為空
# 針對interfield
max_value 最小值
min_value 最大值
# 通用的,大家都有
# 這兩個最重要
read_only 表明該欄位僅用於序列化輸出,預設False(序列化)
write_only 表明該欄位僅用於反序列化輸入,預設False(反序列化)
required 表明該欄位在反序列化時必須輸入,預設True
default 反序列化時使用的預設值
allow_null 表明該欄位是否允許傳入None,預設False
error_messages 包含錯誤編號與錯誤資訊的字典
validators 該欄位使用的驗證器(瞭解)
反序列化,區域性鉤子,全域性鉤子
使用序列化器進行反序列化時,需要對資料進行驗證後,才能獲取驗證成功的資料或儲存成模型類物件。
在獲取反序列化的資料前,必須呼叫is_valid()方法進行驗證,驗證成功返回True,否則返回False。
驗證失敗,可以通過序列化器物件的errors屬性獲取錯誤資訊,返回字典,包含了欄位和欄位的錯誤。如果是非欄位錯誤,可以通過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯誤字典中的鍵名。
驗證成功,可以通過序列化器物件的validated_data屬性獲取資料。
在定義序列化器時,指明每個欄位的序列化型別和選項引數,本身就是一種驗證行為。
1 如果要反序列化,繼承了Serializer,必須重寫create方法
2 使用
# 檢視類
def post(self, request):
publish_ser = serializer.PublishSerializer(data=request.data)
if publish_ser.is_valid(): # 校驗資料
# 直接儲存,儲存到哪個表裡?需要重寫save
publish_ser.save()
return Response(publish_ser.data)
else:
print(publish_ser.errors)
return Response('資料有問題啊')
# 序列化類
def create(self, validated_data):
# 校驗過後的資料
res = models.Publish.objects.create(**validated_data)
return res
"""
父類的save內部呼叫了create,所以我們重寫create
return res 給了self.instance以後,instance就有值了
publish_ser.data,instance就有值呼叫data就能拿到序列化後的資料
is_valid()方法還可以在驗證失敗時丟擲異常serializers.ValidationError,可以通過傳遞raise_exception=True引數開啟,
REST framework接收到此異常,會向前端返回HTTP 400 Bad Request響應。
"""
區域性和全域性鉤子
# validate_欄位名
def validate_name(self, data):
# data就是當前欄位的值
if data.startswith('sb'):
raise ValidationError('不能以sb開頭')
else:
return data
# 在序列化器中需要同時對多個欄位進行比較驗證時,可以定義validate方法來驗證
def validate(self, attrs):
if attrs.get('name') == attrs.get('city'):
raise ValidationError('city和名字不能一樣')
else:
return attrs
模型類序列化器
如果我們想要使用序列化器對應的是Django的模型類,DRF為我們提供了ModelSerializer模型類序列化器來幫助我們快速建立一個Serializer類
ModelSerializer與常規的Serializer相同,但提供了:
- 基於模型類自動生成一系列欄位
- 基於模型類自動為Serializer生成validators,比如unique_together
- 包含預設的create()和update()的實現
檢視類 views.py
class BookView(APIView):
def get(self,request):
qs=models.Book.objects.all()
ser=serializer.BookModelSerializer(instance=qs,many=True)
return Response(ser.data)
def post(self,request):
ser = serializer.BookModelSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
class BookDetailView(APIView):
def get(self,request,id):
book = models.Book.objects.filter(pk=id).first()
ser = serializer.BookModelSerializer(instance=book)
return Response(ser.data)
def put(self,request,id):
book = models.Book.objects.filter(pk=id).first()
ser = serializer.BookModelSerializer(instance=book,data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
def delete(self,request,id):
res = models.Book.objects.filter(pk=id).delete()
if res[0] > 0:
return Response('')
else:
return Response('要刪的不存在')
序列化類 serializers.py
"""
ModelSerializer
使用步驟:
1、新建序列化類,繼承ModelSerializer
2、類中定義和模型表一一對應的欄位,這裡可以定義class Meta() 然後指定模型表model和 對映欄位fields,比Serializer更簡潔
-其中類中的名字可以改變,需要在serializers.CharField()的括號中指定source=某個欄位,建議對映關係
-外來鍵關係的欄位可以用serializers.SerializerMethodField(),需要在下方固定寫 get_欄位名 的方法,
這裡可以寫具體邏輯,最終返回結果就是該欄位的結果
3、當新增資料的時候不需要重寫父類的create方法,這裡ModelSerializer做了封裝
4、當修改資料的時候不需要重寫父類的update方法,這裡ModelSerializer做了封裝
5、當完成這些配置後就可以在檢視類中例項化呼叫了,序列化的時候序列化,反序列化的時候校驗
"""
from rest_framework import serializers
from app import models
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book # model 指明參照哪個模型類
fields = '__all__' # fields 指明為模型類的哪些欄位生成,__all__表名包含所有欄位
# 給欄位類加屬性
extra_kwargs = {
'publish': {'required': True, 'write_only': True},
'authors': {'required': True, 'write_only': True},
}
publish_detail = PublishSerializer(source='publish',read_only=True)
author_list=serializers.ListField(read_only=True)
# 欄位自己的校驗,全域性鉤子,區域性鉤子
"""
使用fields來明確欄位,__all__表名包含所有欄位,也可以寫明具體哪些欄位,如
使用exclude可以明確排除掉哪些欄位
使用extra_kwargs引數為ModelSerializer新增或修改原有的選項引數
"""
表模型 models.py
這是完整的模型表,上面的檢視和序列化類只展示了部分表的增刪改查演示
from django.db import models
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, verbose_name='書名')
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name='價格')
publish_date = models.DateField(verbose_name='出版時間')
publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
authors=models.ManyToManyField(to='Author')
class Meta:
verbose_name_plural = '書籍表'
def __str__(self):
return self.name
# 表模型寫方法,展示更多欄位
# def publish_name(self):
# return self.publish.name
# def publish_name(self):
# return {'name':self.publish.name,'city':self.publish.city}
#
# @property
# def author_list(self):
# return [{'name':author.name,'age':author.age,'id':author.nid} for author in self.authors.all()]
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, verbose_name='名字')
age = models.IntegerField(verbose_name='年齡')
author_detail = models.OneToOneField(to='AuthorDetail',to_field='nid',unique=True,on_delete=models.CASCADE)class Meta:
verbose_name_plural = '作者表'
def __str__(self):
return self.name
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
telephone = models.BigIntegerField(verbose_name='電話')
birthday = models.DateField(verbose_name='生日')
addr = models.CharField(max_length=64, verbose_name='地址')
class Meta:
verbose_name_plural = '作者詳情表'
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, verbose_name='社名')
city = models.CharField(max_length=32, verbose_name='地址')
email = models.EmailField(verbose_name='郵箱')
class Meta:
verbose_name_plural = '出版社表'
def __str__(self):
return self.name
路由urls.py
urlpatterns = [
path('books/',views.BookView.as_view()),
path('books/<int:id>/',views.BookDetailView.as_view()),
]
序列化高階用法
ModelSerializer用的基本就是下面這個方法
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
"""
如果想讓欄位更豐富些,可以採用任意一種方法實現(方法有很多種)
第一種方法:source
publish = serializers.CharField(source='publish.city')
第二種方法:SerializerMethodField
publish=serializers.SerializerMethodField()
def get_publish(self,obj):
return {'name':obj.publish.name,'city':obj.publish.city,'email': obj.publish.email}
第三種方法:在表模型寫方法
def publish_name(self):
return {'name':self.publish.name,'city':self.publish.city}
@property
def author_list(self):
return [{'name':author.name,'age':author.age,'id':author.nid} for author in self.authors.all()]
第四種方法:子序列化
publish = Publishserializer()
"""