什么是rest_framework序列化?
在寫前后端不分離的項目時:
我們有form組件幫我們去做數據校驗
我們有模板語法,從數據庫取出的queryset對象不需要人為去轉格式
當我們寫前后端分離項目的時:
我們需要自己去做數據校驗
我們需要手動去轉數據格式,因為跨平台數據傳輸都用json字符串,不能直接jsonqueryset對象
這個時候你就要想想了,原生django里有這么神奇的組件,那rest_framework里會不會也有這樣的組件呢?嗯...果然有!
這就是rest_framework的序列化組件,下面我們一起來看!
怎么用rest_framework的序列化組件?
首先我們需要從rest_framework導幾個模塊
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即可拿到序列化或者校驗后的數據了
原生Serializer用法:
詳細使用我們來上代碼,我們起一個django項目,在app01里面新建myserializer文件,這里面寫我們定義的序列化類。
看項目應用目錄
路由層
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # /books/的時候表示獲取所有圖書, /book/1/ 表示對id為1的圖書進行操作 url(r'^book/(?P<id>\w+)/$|(?P<type>books)/$', views.Book.as_view()), # 獲取所有出版社信息 url(r'^publish/$', views.Publishs.as_view()), # 對某個出版社進行操作 url(r'^publish/(?P<id>\w+)/$', views.Publish.as_view()), # 作者author部分暫沒寫 ]
模型層
from django.db import models # Create your models here. from django.contrib.auth.models import User,AbstractUser class UserInfo(AbstractUser): phone = models.CharField(max_length=15) avatar = models.FileField(upload_to='static/avatar',default=None) class Book(models.Model): title = models.CharField(max_length=64) price = models.DecimalField(max_digits=10,decimal_places=2) publish_time = models.DateField(null=True) authors = models.ManyToManyField(to='Author') publish = models.ForeignKey(to="Publish") def __str__(self): return self.title class Author(models.Model): name = models.CharField(max_length=64) choices = ((0, '男'), (1, '女'), (2, '保密')) sex = models.IntegerField(choices=choices) info = models.CharField(null=True,max_length=255) def __str__(self): return self.name class Publish(models.Model): name = models.CharField(max_length=32) phone = models.CharField(null=True,max_length=15) address = models.CharField(null=True,max_length=255) def __str__(self): return self.name
序列化組件層
from rest_framework import serializers from app01 import models """ 使用步驟: 1、新建序列化類,繼承Serializer 2、類中定義和模型表一一對應的字段 -其中類中的名字可以改變,需要在serializers.CharField()的括號中指定source=某個字段,建議映射關系 -外鍵關系的字段可以用serializers.SerializerMethodField(),需要在下方固定寫 get_字段名 的方法, 這里可以寫具體邏輯,最終返回結果就是該字段的結果 3、當新增數據的時候需要重寫父類的create方法,邏輯由自己實現,可以參考下面BookSerilizers類中實現的create方法 4、當修改數據的時候需要重寫父類的update方法,邏輯由自己實現,可以參考下面BookSerilizers類中實現的update方法 5、當完成這些配置后就可以在視圖類中實例化調用了,序列化的時候序列化,反序列化的時候校驗 """ class PublishSerilizers(serializers.Serializer): id = serializers.IntegerField(read_only=True) name = serializers.CharField(max_length=32) phone = serializers.CharField(max_length=15) address = serializers.CharField(max_length=255) class AuthorSerilizers(serializers.Serializer): id = serializers.IntegerField(read_only=True) name = serializers.CharField(max_length=32) sex = serializers.CharField() info = serializers.CharField(max_length=255) class BookSerilizers(serializers.Serializer): id = serializers.IntegerField(read_only=True) #read_only 是指當前字段只讀,前端可以不用傳 write_only是指不給前端返回這個字段,但是前端新增和修改必須傳 title = serializers.CharField(max_length=64) price = serializers.DecimalField(max_digits=8,decimal_places=2) publish_time = serializers.DateField() # publish = serializers.CharField(source="publish.name") # source 參數可以指定當下顯示的字段名關聯至模型表里的哪個字段名,當指定了以后當前命名的名字就不能和數據庫里相應的字段名相同了 publish = serializers.SerializerMethodField(allow_null=True) # SerializerMethodField的方式,然后下面定義get_字段名的方法,即可在方法體寫邏輯代碼返回你希望得到值 def get_publish(self,obj): # 這里的obj必傳,obj是當前循環到的數據對象 res = PublishSerilizers(instance=obj.publish) # 序列化當前數據對象關聯的publish表里的相應數據記錄 return res.data authors = serializers.SerializerMethodField(allow_null=True) def get_authors(self,obj): authors_list = obj.authors.all() # author與book是多對多關系,所以需要.all()取到所有的記錄,就是ORM的方法 res = AuthorSerilizers(instance=authors_list,many=True) #由於是序列化多條記錄,指定many=True return res.data def create(self, validated_data): ret = models.Book.objects.create(**validated_data) # 用Django ORM的操作將視圖層傳來的數據新增到數據庫 return ret def update(self, instance, validated_data): # instance是數據庫原來的對象 validated_data是個字典,裝的是校驗完成后的需要修改的數據 instance.name = validated_data.get("name") instance.price = validated_data.get("price") instance.publish_time = validated_data.get("publish_time") instance.publish_id = validated_data.get("publish_id") instance.save() # 逐個替換后保存 instance.authors.set(validated_data.get("authors")) # 修改多對多表記錄,修改需要用set([1,2,4])的形式 return instance
視圖層
from django.shortcuts import render,HttpResponse # Create your views here. 以Book信息為例 from rest_framework.views import APIView,Response from app01 import models from app01 import myserializers from app01 import mymodelSer class Book(APIView): def get(self,request,*args,**kwargs): if kwargs.get("type") == 'books': # 根據路由請求過來的方式來判斷是查詢所有書籍信息還是一本書的信息,這里是查所有的 books = models.Book.objects.all() many= True # 指定序列化參數,如果是序列化多條數據就是True elif kwargs.get("id").isdigit(): # 如果查詢攜帶id,那么就查單本書的 books = models.Book.objects.filter(id=kwargs.get("id")).first() #序列化單條數據的數據要.first() many = False # 指定序列化參數,如果是序列化單條數據就是True res = myserializers.BookSerilizers(instance=books,many=many) # 將數據庫取的queryset對象傳入自己定義的序列化類進行序列化 return Response(res.data) pass def post(self,request,**kwargs): response = {"status_code":100,"msg":"新增成功!"} ret = myserializers.BookSerilizers(data=request.data,many=False) #反序列化前端傳來的數據 if ret.is_valid(): # 數據校驗,當執行這句判斷時,才會去做校驗 print(ret.validated_data) # 校驗后的結果,用ret.validated_data,它會過濾掉不屬於模型表字段的鍵值對,包括前端傳來的publish,authors,因為在序列化類里面這兩個字段用了SerializerMethodField ret.validated_data["publish_id"] = request.data.get("publish_id") #在創建book對象前需要手動叫publish字段的鍵值對添加進去,然后創建book記錄 res = ret.create(ret.validated_data) # 這里的create方法調用的是我們在BookSerilizers里面重寫的父類的create方法 # 在create的時候必須重寫父類的create方法,不然就只能用Django ORM新增,在這里的create里面實際上也是用ORM的新增 res.authors.add(*request.data.get("authors")) #手動用ORM新建多對多第三張表的記錄 res.save() response["data"] = myserializers.BookSerilizers(instance=res).data # res 是數據對象,需要序列化得到數據.data獲取 return Response(response) else: response["status_code"] = 101 response['msg'] = '新增失敗!' response["data"] = ret.errors # 如果校驗不成功,錯誤信息會自動放在errors里,errors = {"name":"局部錯誤信息",..."detail":"全局錯誤信息"} return Response(response) pass def put(self,request,**kwargs): response = {"status_code": 200, "msg": "修改成功!"} obj = models.Book.objects.filter(id = kwargs.get("id")).first() ret = myserializers.BookSerilizers(instance=obj,data=request.data, many=False) # 修改時需要將原數據對象和前端傳來的將要修改的信息傳入BookSerilizers反序列化校驗 if ret.is_valid(): # 校驗成功時 ret.validated_data['publish_id'] = request.data.get("publish_id") ret.validated_data['authors'] = request.data.get("authors") res = ret.update(instance=obj,validated_data=ret.validated_data) # 將數據對象和校驗后的結果傳入更新 response["data"] = ret.validated_data return Response(response) else: response["data"] = ret.errors return Response(response) pass def delete(self,request): pass
Serailizer使用總結:定義序列化類繼承Serailizer,類中定義與模型表一一對應的字段,然后再定義局部鈎子和全局鈎子做校驗用,重寫create和update方法,最后再視圖類中實例化序列化類,通過.data取到序列化后的數據
下面我們來看ModelSerailizer的使用:
路由層
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^publish/$', views.PublishsView.as_view()), url(r'^publish/(?P<pk>\d+)/$', views.PublishView.as_view()), url(r'^book/$', views.BooksView.as_view()), url(r'^book/(?P<pk>\d+)/$', views.BookView.as_view()), url(r'^author/$', views.AuthorsView.as_view()), url(r'^author/(?P<pk>\d+)/$', views.AuthorView.as_view()), ]
模型層
from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here.class Publish(models.Model): name = models.CharField(max_length=32) phone = models.CharField(max_length=15) address = models.CharField(max_length=255) class Book(models.Model): title = models.CharField(max_length=64) price = models.DecimalField(max_digits=8,decimal_places=2) publish = models.ForeignKey(to='Publish') authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32) sex = models.IntegerField(choices=((1,'男'),(0,'女'),(2,'保密')),default=1) info = models.CharField(max_length=255,default='這人很懶,什么都沒寫!')
序列化組件層
from rest_framework.serializers import Serializer,ModelSerializer from rest_framework import serializers from rest_framework.exceptions import ValidationError from app01 import models """ 使用步驟: 1、新建序列化類,繼承ModelSerializer 2、類中定義和模型表一一對應的字段,這里可以定義class Meta() 然后指定模型表model和 映射字段fields,比Serializer更簡潔 -其中類中的名字可以改變,需要在serializers.CharField()的括號中指定source=某個字段,建議映射關系 -外鍵關系的字段可以用serializers.SerializerMethodField(),需要在下方固定寫 get_字段名 的方法, 這里可以寫具體邏輯,最終返回結果就是該字段的結果 3、當新增數據的時候不需要重寫父類的create方法,這里ModelSerializer做了封裝 4、當修改數據的時候不需要重寫父類的update方法,這里ModelSerializer做了封裝 5、當完成這些配置后就可以在視圖類中實例化調用了,序列化的時候序列化,反序列化的時候校驗 """ class PublishSerializer(ModelSerializer): # 自定義序列化類繼承ModelSerializer可以在類里面寫class Meta() class Meta(): # 如果不想每個字段都自己寫,那么這就是固定寫法,在繼承serializer中字段必須自己寫,這是二者的區別 model = models.Publish # 指定需要序列化的模型表 fields = ("__all__") # 指定需要校驗的字段 "__all__" 表示所有字段,也可以指定字段(字段一,字段二)
# exclude = ('name') 和fields用法相反,取除了某個字段以外的字段
# depth = 1 指定數據跨表深度,2 指跨2兩張表,外鍵關系會取到完整信息
def validate_name(self,value): # 局部鈎子 validate_加字段名,需要給定形參,這個形參就是字段值 if value.startswith("sb"): raise ValidationError("出版社名稱含有敏感詞匯") # 拋出異常,但這個異常是在errors信息中返回給前端,不會在后端報出 return value # 對哪個字段進行校驗之后需要將該字段值返回 def validate_phone(self,value): if not value.isdigit(): raise ValidationError("出版社聯系方式不合法!") return value def validate(self, attrs): # 全局鈎子 做全局性的數據校驗,attrs即需要校驗的數據{"name":'xxx',...} name = attrs.get("name") phone = attrs.get("phone") address = attrs.get("address") if (not phone) and (not address): raise ValidationError("出版社聯系方式和地址必須選填一項") return attrs class BookSerializer(ModelSerializer): class Meta(): model = models.Book fields = ('title','price','authors','publish') #指定序列化的字段 #加上以下這一段在get請求數據的時候會拿到關聯字段的詳細信息,但是在存的時候會報錯 ---方便取,不方便存,存需要另外定義反序列化類 #如果去掉下面這一段在get請求的時候會拿到關聯字段的id返回前端,存的時候不會報錯 # ======================================================= authors = serializers.SerializerMethodField() def get_authors(self,obj): res = AuthorSerializer(instance=obj.authors.all(),many=True) return res.data publish = serializers.SerializerMethodField() def get_publish(self,obj): res = PublishSerializer(instance=obj.publish) return res.data # ================================================== def validate_title(self,value): # 局部鈎子校驗 if value.startswith("sb"): raise ValidationError("書名不能包含敏感詞匯") return value def validate_price(self,value): # 全局鈎子校驗 if not value: raise ValidationError("圖書價格不能為空!") try: float(value) except ValueError: raise ValidationError("請輸入合法的圖書價格!") return value class AuthorSerializer(ModelSerializer): class Meta(): model = models.Author fields = ('name','sex','info') # 以下如果自定義sex字段的取值內容,取的時候會取到 '男','女','其他',但存的時候會報錯 # 如果不自定義的話,取的是數據庫存儲的內容 1,0,2 存可以直接通過0,1,2去存,也就是前端只需要傳sex的數字值即可存 # ============================================================ sex = serializers.CharField(source="get_sex_display") # ============================================================
視圖層
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from app01 import models from app01.Object_Seriailzers import PublishSerializer,BookSerializer,AuthorSerializer # Create your views here. #出版社接口 class PublishsView(APIView): def get(self,request,*args,**kwargs): response = {"status_code":100,"msg":"查詢成功!",'data':""} queryset = models.Publish.objects.all() # 對多條數據進行序列化需要制定many=True ret = PublishSerializer(instance=queryset,many=True) response['data'] = ret.data return Response(response) pass def post(self,request,*args,**kwargs): response = {"status_code":200,'msg':"新增成功!","data":''} ret = PublishSerializer(data=request.data) if ret.is_valid(): # 這里會執行所有的校驗,系統及自定義 ret.save() # 這里的保存就會執行數據庫的保存,不需要去自己寫create方法 response['data'] = ret.data #如果是繼承原生的Serailizer,獲取到過濾后的數據要通過ret.validated_data取 else: response["status_code"] = 201 response['msg'] = '新增失敗!' response['data'] = ret.errors return Response(response) pass class PublishView(APIView): def get(self,request,pk,*args,**kwargs): response = {"status_code":100,'msg':'查詢成功!','data':''} ret = PublishSerializer(models.Publish.objects.filter(pk=pk).first()) response['data'] = ret.data # 取到序列化后的對象 return Response(response) pass def put(self,request,pk,*args,**kwargs): response = {"status_code":200,'msg':'修改成功!','data':''} # 先查出對象 query = models.Publish.objects.filter(pk=pk).first() # 將數據庫對象和前端請求的數據交由PublishSerializer序列化 ret = PublishSerializer(instance=query,data=request.data) if ret.is_valid(): #判斷是否校驗通過 # 將數據庫對象和前端請求的數據交由update更新,update內部已經封裝更新和保存操作 # 如果涉及到多表更新或其他復雜操作,可以在PublishSerializer中自定義update方法,update返回的是更新后的對象 data = ret.update(instance=query,validated_data=ret.validated_data) # 將修改后的數據返回,注意取ret.data是過濾后的數據,如果是繼承原生的Serailizer取過濾后的數據是ret.validated_data取 response['data'] = ret.data else: response['status_code'] = 201 response['msg'] = '修改失敗!' response['data'] = ret.errors return Response(response) pass def delete(self,request,pk,*args,**kwargs): response = {'status_code':200,'msg':'刪除成功!'} models.Publish.objects.filter(pk=pk).first().delete() return Response(response) pass # 圖書接口 class BooksView(APIView): def get(self, request,*args,**kwargs): response = {"status_code":200,'msg':'查詢成功!','data':''} queryset = models.Book.objects.all() ret = BookSerializer(instance=queryset,many=True) response['data'] = ret.data return Response(response) pass def post(self, request,*args,**kwargs): response = {"status_code": 200, 'msg': '新增成功!', 'data': ''} ret = BookSerializer(data=request.data) if ret.is_valid(): obj = ret.save() response['data'] = ret.data else: response['status_code'] = 201 response['msg'] = '新增失敗!' response['data'] = ret.errors return Response(response) pass class BookView(APIView): def get(self, request,pk,*args,**kwargs): response = {"status_code": 200, 'msg': '查詢成功!', 'data': ''} ret = BookSerializer(instance=models.Book.objects.filter(pk=pk).first()) response['data'] = ret.data return Response(response) pass def put(self, request,pk): response = {"status_code": 200, 'msg': '更新成功!', 'data': ''} query = models.Book.objects.filter(pk=pk).first() ret = BookSerializer(instance=query,data=request.data) if ret.is_valid(): # 這里的if也可以不需要,只需要在is_valid()中配置raise_exception=True即可,
# 如果校驗不通過,內部會直接返回錯誤信息給前端,下面代碼不會走 obj = ret.save() response['data'] = BookSerializer(instance=obj).data else: response['status_code'] = 201 response['msg'] = '更新失敗!' response['data'] = ret.errors return Response(response) pass def delete(self, request,pk): response = {"status_code": 200, 'msg': '刪除成功!', 'data': ''} models.Book.objects.filter(pk=pk).first().delete() return Response(response) pass # 作者接口 class AuthorsView(APIView): def get(self,request): response = {"status_code": 200, 'msg': '查詢成功!', 'data': ''} queryset = models.Author.objects.all() ret = AuthorSerializer(instance=queryset,many=True) response['data'] = ret.data return Response(response) pass def post(self,request): response = {"status_code": 200, 'msg': '新增成功!', 'data': ''} ret = AuthorSerializer(data=request.data) if ret.is_valid(): obj = ret.save() response['data'] = AuthorSerializer(instance=obj).data else: response['msg'] = '新增失敗!' response['data'] = ret.errors return Response(response) pass class AuthorView(APIView): def get(self,request,pk,*args,**kwargs): response = {"status_code": 200, 'msg': '查詢成功!', 'data': ''} ret = AuthorSerializer(instance=models.Author.objects.filter(pk=pk).first()) response['data'] = ret.data return Response(response) pass def put(self,request,pk,*args,**kwargs): response = {"status_code": 200, 'msg': '更新成功!', 'data': ''} query = models.Author.objects.filter(pk=pk).first() ret = AuthorSerializer(instance=query, data=request.data) if ret.is_valid(): obj = ret.save() response['data'] = ret.data else: response['status_code'] = 201 response['msg'] = '更新失敗!' response['data'] = ret.errors return Response(response) pass def delete(self,request , pk, *args, **kwargs): response = {"status_code": 200, 'msg': '刪除成功!', 'data': ''} models.Author.objects.filter(pk=pk).first().delete() return Response(response) pass
ModelSerailizer總結: 在Serailizer的基礎上進行了進一步的封裝,主要體現在3個地方:
1、在序列化類中定義字段不需要全部都自己定義映射關系,只需要配置class Meta 里的模型表model 和字段fields
2、在存和修改的時候不需要在序列化類中實現父類的create和update方法,ModelSerailizer已經做了封裝,當然,這是只針對單表的操作,如果涉及多表的操作,我們也可以自己重寫create和update方法
3、在校驗數據成功后只需要通過ret.save()即可執行create操作,ret.update即可執行更新操作
這里的坑點:
其實在代碼里也總結了,當我們在用SerailizerMethodField()時,可以通過定義方法序列化數據庫的關聯表數據,加上我們自己的邏輯並得到返回值,但是這樣會帶來一個問題,在反序列化的時候校驗會把這些字段的數據過濾掉,導致create和update出現報錯,當然我們可以通過將這些數據添加到validated_data中來達到目的,但這終究不是完美的解決辦法,小編才疏學淺,還望有大神路過指點一二