(三) DRF 序列化

1、單表的GET和POST:

使用serializers序列化,針對每個表,須要單獨寫函數。通常會寫在views.py裏面,可是這樣作,會致使整個文件代碼過長。須要分離出來!django

在app01(應用名)目錄下,建立文件app01_serializers.py,表示自定義序列化api

from app01 import models from rest_framework import serializers # 序列化評論的類 class CommentSerializer(serializers.ModelSerializer): class Meta: model = models.Comment # Comment表 fields = "__all__" # 序列化全部字段 depth = 2  # 深度爲2

views.py中app

from rest_framework.views import APIView from app01 import app01_serializers # 導入自定義的序列化 class Comment(APIView):
def
get(self, request): res = {"code":0} # 默認狀態 all_comment = models.Comment.objects.all() # print(all_comment) # 序列化,many=True表示返回多條 ser_obj = app01_serializers.CommentSerializer(all_comment, many=True) res["data"] = ser_obj.data return JsonResponse(res)

 

2、Response

Rest framework 引入了Response對象,它是一個TemplateResponse類型,並根據客戶端需求正確返回須要的類型。ide

使用前,須要導入模塊Response函數

from rest_framework.response import Response

舉例:post

修改視圖Comment中的get方法,將JsonResponse改爲Responseui

 

 
  
from rest_framework.views import APIView
from app01 import app01_serializers  # 導入自定義的序列化
from rest_framework.response import Response
# Create your views here. class Comment(APIView): def get(self, request): res = {"code":0} # 默認狀態 all_comment = models.Comment.objects.all() # print(all_comment) # 序列化,many=True表示返回多條 ser_obj = app01_serializers.CommentSerializer(all_comment, many=True) res["data"] = ser_obj.data return Response(res)

 

3、serializers校驗

舉例:判斷空數據url

修改views.py,添加post邏輯代碼。注意:使用is_valid校驗spa

from rest_framework.views import APIView from app01 import app01_serializers # 導入自定義的序列化 from rest_framework.response import Response # Create your views here. class Comment(APIView): def get(self, request): res = {"code":0} # 默認狀態 all_comment = models.Comment.objects.all() # print(all_comment) # 序列化,many=True表示返回多條 ser_obj = app01_serializers.CommentSerializer(all_comment, many=True) res["data"] = ser_obj.data return Response(res) def post(self, request): res = {"code": 0} # 去提交的數據 comment_data = self.request.data # 對用戶提交的數據作校驗 ser_obj = app01_serializers.CommentSerializer(data=comment_data) if ser_obj.is_valid(): # 表示數據沒問題,能夠建立 pass else: # 表示數據有問題 res["code"] = 1 res["error"] = ser_obj.errors return Response(res)

使用postman發送一個空數據的post請求rest

它返回This field is required,表示次字段不能爲空!

錯誤信息中文顯示

修改app01_serializers.py,使用extra_kwargs指定錯誤信息

from app01 import models from rest_framework import serializers # 序列化評論的類 class CommentSerializer(serializers.ModelSerializer): class Meta: model = models.Comment # Comment表 fields = "__all__" # 序列化全部字段 depth = 2 # 深度爲2 # 定義額外的參數 extra_kwargs = { "content": { "error_messages": { "required": '內容不能爲空', } }, }

重啓django,從新發送空的post請求

 

4、外鍵的GET和POST

序列化校驗

上面雖然只發送了content參數,就讓經過了,顯然不合理!爲何呢?

由於app01_comment表有2個字段,content和article。這2個字段都應該校驗纔對!

由於serializers默認校驗時,排除了外鍵字段。好比article

要對外鍵進行校驗,必須在extra_kwargs中指定外鍵字段

修改app01_serializers.py,注意關閉depth參數

當序列化類MATE中定義了depth時,這個序列化類中引用字段(外鍵)則自動變爲只讀,因此進行更新或者建立操做的時候不能使用此序列化類

大概意思就是,使用了depth參數,會忽略外鍵字段

完整代碼以下:

from app01 import models from rest_framework import serializers # 序列化評論的類 class CommentSerializer(serializers.ModelSerializer): class Meta: model = models.Comment # Comment表 fields = "__all__" # 序列化全部字段 # depth = 2 # 深度爲2 # 定義額外的參數 extra_kwargs = { "content": { "error_messages": { "required": '內容不能爲空', } }, "article": { "error_messages": { "required": '文章不能爲空' } } }

再次發送post請求,仍是隻有一個參數content

查看執行結果:

發送正確的2個參數

 

查看結果

read_only=True

read_only:True表示不容許用戶本身上傳,只能用於api的輸出。若是某個字段設置了read_only=True,那麼就不須要進行數據驗證,只會在返回時,將這個字段序列化後返回

 舉例:容許article不校驗

修改app01_serializers.py,加入一行代碼

article = serializers.SerializerMethodField(read_only=True)
from app01 import models from rest_framework import serializers # 序列化評論的類 class CommentSerializer(serializers.ModelSerializer): article = serializers.SerializerMethodField(read_only=True) class Meta: model = models.Comment # Comment表 fields = "__all__" # 序列化全部字段 # depth = 2 # 深度爲2 # 定義額外的參數 extra_kwargs = { "content": { "error_messages": { "required": '內容不能爲空', } }, "article": { "error_messages": { "required": '文章不能爲空' } } }

保存POST數據

修改views.py,在post方法中,將pass改爲ser_obj.save(),完整代碼以下:

from rest_framework.views import APIView from app01 import app01_serializers # 導入自定義的序列化 from rest_framework.response import Response # Create your views here. class Comment(APIView): def get(self, request): res = {"code":0} # 默認狀態 all_comment = models.Comment.objects.all() # print(all_comment) # 序列化,many=True表示返回多條 ser_obj = app01_serializers.CommentSerializer(all_comment, many=True) res["data"] = ser_obj.data return Response(res) def post(self, request): res = {"code": 0} # 去提交的數據 comment_data = self.request.data # 對用戶提交的數據作校驗 ser_obj = app01_serializers.CommentSerializer(data=comment_data) if ser_obj.is_valid(): # 表示數據沒問題,能夠建立 ser_obj.save() else: # 表示數據有問題 res["code"] = 1 res["error"] = ser_obj.errors return Response(res)

修改app01_serializers.py,註釋掉read_only=True

發送2個正確的參數

查看app01_comment表記錄,發現多了一條記錄

爲何直接save,就能夠保存了呢?

由於它將校驗過的數據傳過去了,就好像form組件中的self.cleaned_data同樣

本質上仍是調用ORM的create()方法

 

5、非serializer 的驗證條件

好比重置密碼、修改密碼都須要手機驗證碼。可是用戶 model 裏面並無驗證碼這個選項

須要使用validate,用於作校驗的鉤子函數,相似於form組件的clean_字段名

使用時,須要導入模塊,用來輸出錯誤信息

from rest_framework.validators import ValidationError

局部鉤子

validate_字段名,表示局部鉤子。

舉例:評論的內容中,不能包含 "草"

修改app01_serializers.py,校驗評論內容

from app01 import models from rest_framework import serializers from rest_framework.validators import ValidationError # 序列化評論的類 class CommentSerializer(serializers.ModelSerializer): # article = serializers.SerializerMethodField(read_only=True) # 用於作校驗的鉤子函數,相似於form組件的clean_字段名 def validate_content(self, value): if '' in value: raise ValidationError('不符合社會主義核心價值觀!') else: return value class Meta: model = models.Comment # Comment表 fields = "__all__" # 序列化全部字段 # depth = 2 # 深度爲2 # 定義額外的參數 extra_kwargs = { "content": { "error_messages": { "required": '內容不能爲空', } }, "article": { "error_messages": { "required": '文章不能爲空' } } }

使用postman發送包含關鍵字的評論

查看返回結果:

全局鉤子

validate,表示全局鉤子。

好比在用戶註冊時,咱們須要填寫驗證碼,這個驗證碼只須要驗證,不須要保存到用戶這個Model中:

def validate(self, attrs): del attrs["code"] return attrs

 

6、超連接的序列化

HyperlinkedModelSerializer類相似於ModelSerializer類,不一樣之處在於它使用超連接來表示關聯關係而不是主鍵。

默認狀況下序列化器將包含一個url字段而不是主鍵字段。

url字段將使用HyperlinkedIdentityField字段來表示,模型的任何關聯都將使用HyperlinkedRelatedField字段來表示。

你能夠經過將主鍵添加到fields選項中來顯式的包含,例如:

class AccountSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Account fields = ('url', 'id', 'account_name', 'users', 'created')

絕對和相對URL

當實例化一個HyperlinkedModelSerializer時,你必須在序列化器的上下文中包含當前的request值,例如:

serializer = AccountSerializer(queryset, context={'request': request})

這樣作將確保超連接能夠包含恰當的主機名,一邊生成徹底限定的URL,例如:

http://api.example.com/accounts/1/

而不是相對的URL,例如:

/accounts/1/

若是你真的要使用相對URL,你應該明確的在序列化器上下文中傳遞一個{'request': None}

 

需求:要求api返回結果中,school展現的是超連接

修改app01_urls.py,增長路由

urlpatterns = [ # 文章 url(r'article/', views.Article.as_view()), url(r'article/(?P<pk>\d+)', views.ArticleDetail.as_view(), name='article-detail'), # 學校 url(r'school/(?P<id>\d+)', views.SchoolDetail.as_view(), name='school-detail'), # 評論 url(r'comment/', views.Comment.as_view()), ]

指定name是爲作反向連接,它能解析出絕對url

序列化

修改app01_serializers.py

from app01 import models from rest_framework import serializers from rest_framework.validators import ValidationError # 序列化評論的類 class CommentSerializer(serializers.ModelSerializer): # article = serializers.SerializerMethodField(read_only=True) # 用於作校驗的鉤子函數,相似於form組件的clean_字段名 def validate_content(self, value): if '' in value: raise ValidationError('不符合社會主義核心價值觀!') else: return value #全局的鉤子 def validate(self, attrs): # self.validated_data # 通過校驗的數據 相似於form組件中的cleaned_data # 全局鉤子 pass class Meta: model = models.Comment # Comment表 fields = "__all__" # 序列化全部字段 # depth = 2 # 深度爲2 # 定義額外的參數 extra_kwargs = { "content": { "error_messages": { "required": '內容不能爲空', } }, "article": { "error_messages": { "required": '文章不能爲空' } } } # 文章的序列化類 class ArticleModelSerializer(serializers.ModelSerializer): class Meta: model = models.Article # 綁定的ORM類是哪個 fields = "__all__"  # ["id", "title", "type"] # depth = 1 # 官方推薦不超過10層 # 文章超連接序列化 class ArticleHyperLinkedSerializer(serializers.HyperlinkedModelSerializer): school = serializers.HyperlinkedIdentityField(view_name='school-detail', lookup_url_kwarg='id') class Meta: model = models.Article # 綁定的ORM類是哪個 fields =  ["id", "title", "type", "school"] # 學校的序列化 class SchoolSerializer(serializers.ModelSerializer): class Meta: model = models.School fields = "__all__"

參數解釋:

source 表示來源

lookup_field 表示查找字段,默認使用的pk, 指的是反向生成URL的時候, 路由中分組命名匹配的value

lookup_url_kwarg 表示路由查找的參數,pk表示主鍵, 默認使用pk,指的是反向生成URL的時候 路由中的分組命名匹配的key

view_name  它是指urls定義的name值,必定要一一對應。 默認使用 表名-detail

 

修改views.py,增長視圖函數

from rest_framework.views import APIView from app01 import app01_serializers # 導入自定義的序列化 from rest_framework.response import Response # Create your views here. class Comment(APIView): def get(self, request): res = {"code":0} # 默認狀態 all_comment = models.Comment.objects.all() # print(all_comment) # 序列化,many=True表示返回多條 ser_obj = app01_serializers.CommentSerializer(all_comment, many=True) res["data"] = ser_obj.data return Response(res) def post(self, request): res = {"code": 0} # 去提交的數據 comment_data = self.request.data # 對用戶提交的數據作校驗 ser_obj = app01_serializers.CommentSerializer(data=comment_data) if ser_obj.is_valid(): # 表示數據沒問題,能夠建立 ser_obj.save() else: # 表示數據有問題 res["code"] = 1 res["error"] = ser_obj.errors return Response(res) # return HttpResponse("建立新評論") def put(self, request): return HttpResponse("修改評論") def delete(self, request): return HttpResponse("刪除評論") # 文章CBV class Article(APIView): def get(self, request): res = {"code": 0} all_article = models.Article.objects.all() ser_obj = app01_serializers.ArticleHyperLinkedSerializer(all_article, many=True, context={'request': request}) res["data"] = ser_obj.data return Response(res) def post(self, request): res = {"code": 0} ser_obj = app01_serializers.ArticleModelSerializer(data=self.request.data) if ser_obj.is_valid(): ser_obj.save() else: res["code"] = 1 res["error"] = ser_obj.errors return Response(res) # 文章詳情CBV class ArticleDetail(APIView): def get(self, request, pk): res = {"code": 0} article_obj = models.Article.objects.filter(pk=pk).first() # 序列化 ser_obj = app01_serializers.ArticleHyperLinkedSerializer(article_obj, context={'request': request}) res["data"] = ser_obj.data return Response(res) # 學校詳情CBV class SchoolDetail(APIView): def get(self, request, id): res = {"code": 0} school_obj = models.School.objects.filter(pk=id).first() ser_obj = app01_serializers.SchoolSerializer(school_obj, context={'request': request}) res["data"] = ser_obj.data return Response(res)
View Code

參數解釋: 

id 表示參數,它和url的參數,是一一對應的

content 表示上下文

 

重啓django項目,訪問網頁:

http://127.0.0.1:8000/api/article/1

效果以下:

點擊第一個連接,效果以下:

相關文章
相關標籤/搜索